mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-04-23 05:47:11 -04:00
Convert SettingsToolbar to TypeScript
This commit is contained in:
parent
95929dd9c2
commit
fd09ca6e71
31 changed files with 326 additions and 552 deletions
|
@ -16,10 +16,10 @@ const BUTTON_WIDTH = parseInt(dimensions.toolbarButtonWidth);
|
|||
const SEPARATOR_MARGIN = parseInt(dimensions.toolbarSeparatorMargin);
|
||||
const SEPARATOR_WIDTH = 2 * SEPARATOR_MARGIN + 1;
|
||||
|
||||
interface PageToolbarSectionProps {
|
||||
export interface PageToolbarSectionProps {
|
||||
children?:
|
||||
| (ReactElement<PageToolbarButtonProps> | ReactElement<never>)
|
||||
| (ReactElement<PageToolbarButtonProps> | ReactElement<never>)[];
|
||||
| (ReactElement<PageToolbarButtonProps> | ReactElement<never> | null)
|
||||
| (ReactElement<PageToolbarButtonProps> | ReactElement<never> | null)[];
|
||||
alignContent?: Extract<Align, keyof typeof styles>;
|
||||
collapseButtons?: boolean;
|
||||
}
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './AdvancedSettingsButton.css';
|
||||
|
||||
function AdvancedSettingsButton(props) {
|
||||
const {
|
||||
advancedSettings,
|
||||
onAdvancedSettingsPress,
|
||||
showLabel
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={styles.button}
|
||||
title={advancedSettings ? translate('ShownClickToHide') : translate('HiddenClickToShow')}
|
||||
onPress={onAdvancedSettingsPress}
|
||||
>
|
||||
<Icon
|
||||
name={icons.ADVANCED_SETTINGS}
|
||||
size={21}
|
||||
/>
|
||||
|
||||
<span
|
||||
className={classNames(
|
||||
styles.indicatorContainer,
|
||||
'fa-layers fa-fw'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
className={styles.indicatorBackground}
|
||||
name={icons.CIRCLE}
|
||||
size={16}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
className={advancedSettings ? styles.enabled : styles.disabled}
|
||||
name={advancedSettings ? icons.CHECK : icons.CLOSE}
|
||||
size={10}
|
||||
/>
|
||||
</span>
|
||||
|
||||
{
|
||||
showLabel ?
|
||||
<div className={styles.labelContainer}>
|
||||
<div className={styles.label}>
|
||||
{advancedSettings ? translate('HideAdvanced') : translate('ShowAdvanced')}
|
||||
</div>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
AdvancedSettingsButton.propTypes = {
|
||||
advancedSettings: PropTypes.bool.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
showLabel: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
AdvancedSettingsButton.defaultProps = {
|
||||
showLabel: true
|
||||
};
|
||||
|
||||
export default AdvancedSettingsButton;
|
67
frontend/src/Settings/AdvancedSettingsButton.tsx
Normal file
67
frontend/src/Settings/AdvancedSettingsButton.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { toggleAdvancedSettings } from 'Store/Actions/settingsActions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './AdvancedSettingsButton.css';
|
||||
|
||||
interface AdvancedSettingsButtonProps {
|
||||
showLabel: boolean;
|
||||
}
|
||||
|
||||
function AdvancedSettingsButton({ showLabel }: AdvancedSettingsButtonProps) {
|
||||
const showAdvancedSettings = useSelector(
|
||||
(state: AppState) => state.settings.advancedSettings
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
dispatch(toggleAdvancedSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={styles.button}
|
||||
title={
|
||||
showAdvancedSettings
|
||||
? translate('ShownClickToHide')
|
||||
: translate('HiddenClickToShow')
|
||||
}
|
||||
onPress={handlePress}
|
||||
>
|
||||
<Icon name={icons.ADVANCED_SETTINGS} size={21} />
|
||||
|
||||
<span
|
||||
className={classNames(styles.indicatorContainer, 'fa-layers fa-fw')}
|
||||
>
|
||||
<Icon
|
||||
className={styles.indicatorBackground}
|
||||
name={icons.CIRCLE}
|
||||
size={16}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
className={showAdvancedSettings ? styles.enabled : styles.disabled}
|
||||
name={showAdvancedSettings ? icons.CHECK : icons.CLOSE}
|
||||
size={10}
|
||||
/>
|
||||
</span>
|
||||
|
||||
{showLabel ? (
|
||||
<div className={styles.labelContainer}>
|
||||
<div className={styles.label}>
|
||||
{showAdvancedSettings
|
||||
? translate('HideAdvanced')
|
||||
: translate('ShowAdvanced')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdvancedSettingsButton;
|
|
@ -5,7 +5,7 @@ import PageContent from 'Components/Page/PageContent';
|
|||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import ParseToolbarButton from 'Parse/ParseToolbarButton';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import CustomFormatsConnector from './CustomFormats/CustomFormatsConnector';
|
||||
import ManageCustomFormatsToolbarButton from './CustomFormats/Manage/ManageCustomFormatsToolbarButton';
|
||||
|
@ -13,9 +13,7 @@ import ManageCustomFormatsToolbarButton from './CustomFormats/Manage/ManageCusto
|
|||
function CustomFormatSettingsPage() {
|
||||
return (
|
||||
<PageContent title={translate('CustomFormatsSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
<SettingsToolbar
|
||||
showSave={false}
|
||||
additionalButtons={
|
||||
<>
|
||||
|
|
|
@ -5,7 +5,7 @@ import PageContentBody from 'Components/Page/PageContentBody';
|
|||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import DownloadClientsConnector from './DownloadClients/DownloadClientsConnector';
|
||||
import ManageDownloadClientsModal from './DownloadClients/Manage/ManageDownloadClientsModal';
|
||||
|
@ -71,7 +71,7 @@ class DownloadClientSettings extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('DownloadClientSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
additionalButtons={
|
||||
|
|
|
@ -38,7 +38,6 @@ class EditDownloadClientModalContent extends Component {
|
|||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onAdvancedSettingsPress,
|
||||
onDeleteDownloadClientPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
@ -202,8 +201,6 @@ class EditDownloadClientModalContent extends Component {
|
|||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
|
@ -247,7 +244,6 @@ EditDownloadClientModalContent.propTypes = {
|
|||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteDownloadClientPress: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import {
|
|||
saveDownloadClient,
|
||||
setDownloadClientFieldValue,
|
||||
setDownloadClientValue,
|
||||
testDownloadClient,
|
||||
toggleAdvancedSettings
|
||||
testDownloadClient
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditDownloadClientModalContent from './EditDownloadClientModalContent';
|
||||
|
@ -29,8 +28,7 @@ const mapDispatchToProps = {
|
|||
setDownloadClientValue,
|
||||
setDownloadClientFieldValue,
|
||||
saveDownloadClient,
|
||||
testDownloadClient,
|
||||
toggleAdvancedSettings
|
||||
testDownloadClient
|
||||
};
|
||||
|
||||
class EditDownloadClientModalContentConnector extends Component {
|
||||
|
@ -63,10 +61,6 @@ class EditDownloadClientModalContentConnector extends Component {
|
|||
this.props.testDownloadClient({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -76,7 +70,6 @@ class EditDownloadClientModalContentConnector extends Component {
|
|||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
|
@ -94,7 +87,6 @@ EditDownloadClientModalContentConnector.propTypes = {
|
|||
setDownloadClientFieldValue: PropTypes.func.isRequired,
|
||||
saveDownloadClient: PropTypes.func.isRequired,
|
||||
testDownloadClient: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AnalyticSettings from './AnalyticSettings';
|
||||
import BackupSettings from './BackupSettings';
|
||||
|
@ -113,7 +113,7 @@ class GeneralSettings extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('GeneralSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
{...otherProps}
|
||||
/>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import PageContentBody from 'Components/Page/PageContentBody';
|
|||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import ImportListExclusions from './ImportListExclusions/ImportListExclusions';
|
||||
import ImportListsConnector from './ImportLists/ImportListsConnector';
|
||||
|
@ -81,7 +81,7 @@ class ImportListSettings extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('ImportListSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
additionalButtons={
|
||||
|
|
|
@ -39,7 +39,6 @@ function EditImportListModalContent(props) {
|
|||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onAdvancedSettingsPress,
|
||||
onDeleteImportListPress,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
@ -292,7 +291,6 @@ function EditImportListModalContent(props) {
|
|||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
|
@ -335,7 +333,6 @@ EditImportListModalContent.propTypes = {
|
|||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteImportListPress: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import {
|
|||
saveImportList,
|
||||
setImportListFieldValue,
|
||||
setImportListValue,
|
||||
testImportList,
|
||||
toggleAdvancedSettings
|
||||
testImportList
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditImportListModalContent from './EditImportListModalContent';
|
||||
|
@ -29,8 +28,7 @@ const mapDispatchToProps = {
|
|||
setImportListValue,
|
||||
setImportListFieldValue,
|
||||
saveImportList,
|
||||
testImportList,
|
||||
toggleAdvancedSettings
|
||||
testImportList
|
||||
};
|
||||
|
||||
class EditImportListModalContentConnector extends Component {
|
||||
|
@ -63,10 +61,6 @@ class EditImportListModalContentConnector extends Component {
|
|||
this.props.testImportList({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -76,7 +70,6 @@ class EditImportListModalContentConnector extends Component {
|
|||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
|
@ -94,7 +87,6 @@ EditImportListModalContentConnector.propTypes = {
|
|||
setImportListFieldValue: PropTypes.func.isRequired,
|
||||
saveImportList: PropTypes.func.isRequired,
|
||||
testImportList: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import PageContentBody from 'Components/Page/PageContentBody';
|
|||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import IndexersConnector from './Indexers/IndexersConnector';
|
||||
import ManageIndexersModal from './Indexers/Manage/ManageIndexersModal';
|
||||
|
@ -70,7 +70,7 @@ class IndexerSettings extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('IndexerSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
additionalButtons={
|
||||
|
|
|
@ -33,7 +33,6 @@ function EditIndexerModalContent(props) {
|
|||
onSavePress,
|
||||
onTestPress,
|
||||
onDeleteIndexerPress,
|
||||
onAdvancedSettingsPress,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
|
@ -222,8 +221,6 @@ function EditIndexerModalContent(props) {
|
|||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
|
@ -266,7 +263,6 @@ EditIndexerModalContent.propTypes = {
|
|||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteIndexerPress: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { saveIndexer, setIndexerFieldValue, setIndexerValue, testIndexer, toggleAdvancedSettings } from 'Store/Actions/settingsActions';
|
||||
import { saveIndexer, setIndexerFieldValue, setIndexerValue, testIndexer } from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditIndexerModalContent from './EditIndexerModalContent';
|
||||
|
||||
|
@ -23,8 +23,7 @@ const mapDispatchToProps = {
|
|||
setIndexerValue,
|
||||
setIndexerFieldValue,
|
||||
saveIndexer,
|
||||
testIndexer,
|
||||
toggleAdvancedSettings
|
||||
testIndexer
|
||||
};
|
||||
|
||||
class EditIndexerModalContentConnector extends Component {
|
||||
|
@ -57,10 +56,6 @@ class EditIndexerModalContentConnector extends Component {
|
|||
this.props.testIndexer({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -70,7 +65,6 @@ class EditIndexerModalContentConnector extends Component {
|
|||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
|
@ -86,7 +80,6 @@ EditIndexerModalContentConnector.propTypes = {
|
|||
item: PropTypes.object.isRequired,
|
||||
setIndexerValue: PropTypes.func.isRequired,
|
||||
setIndexerFieldValue: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
saveIndexer: PropTypes.func.isRequired,
|
||||
testIndexer: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
|
|
|
@ -11,7 +11,7 @@ import PageContent from 'Components/Page/PageContent';
|
|||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import RootFolders from 'RootFolder/RootFolders';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import Naming from './Naming/Naming';
|
||||
import AddRootFolder from './RootFolder/AddRootFolder';
|
||||
|
@ -120,7 +120,7 @@ class MediaManagement extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('MediaManagementSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
advancedSettings={advancedSettings}
|
||||
{...otherProps}
|
||||
onSavePress={onSavePress}
|
||||
|
|
|
@ -83,7 +83,7 @@ function Naming() {
|
|||
dispatch(fetchNamingExamples());
|
||||
|
||||
return () => {
|
||||
dispatch(clearPendingChanges({ section: SECTION }));
|
||||
dispatch(clearPendingChanges({ section: 'settings.naming' }));
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import Metadatas from './Metadata/Metadatas';
|
||||
|
||||
function MetadataSettings() {
|
||||
return (
|
||||
<PageContent title={translate('MetadataSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
showSave={false}
|
||||
/>
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import TheTvdb from './TheTvdb';
|
||||
|
||||
function MetadataSourceSettings() {
|
||||
return (
|
||||
<PageContent title={translate('MetadataSourceSettings')} >
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
showSave={false}
|
||||
/>
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NotificationsConnector from './Notifications/NotificationsConnector';
|
||||
|
||||
function NotificationSettings() {
|
||||
return (
|
||||
<PageContent title={translate('ConnectSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
showSave={false}
|
||||
/>
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ function EditNotificationModalContent(props) {
|
|||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onAdvancedSettingsPress,
|
||||
onDeleteNotificationPress,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
@ -139,8 +138,6 @@ function EditNotificationModalContent(props) {
|
|||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
|
@ -183,7 +180,6 @@ EditNotificationModalContent.propTypes = {
|
|||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteNotificationPress: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import {
|
|||
saveNotification,
|
||||
setNotificationFieldValues,
|
||||
setNotificationValue,
|
||||
testNotification,
|
||||
toggleAdvancedSettings
|
||||
testNotification
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditNotificationModalContent from './EditNotificationModalContent';
|
||||
|
@ -29,8 +28,7 @@ const mapDispatchToProps = {
|
|||
setNotificationValue,
|
||||
setNotificationFieldValues,
|
||||
saveNotification,
|
||||
testNotification,
|
||||
toggleAdvancedSettings
|
||||
testNotification
|
||||
};
|
||||
|
||||
class EditNotificationModalContentConnector extends Component {
|
||||
|
@ -63,10 +61,6 @@ class EditNotificationModalContentConnector extends Component {
|
|||
this.props.testNotification({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -76,7 +70,6 @@ class EditNotificationModalContentConnector extends Component {
|
|||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
|
@ -94,7 +87,6 @@ EditNotificationModalContentConnector.propTypes = {
|
|||
setNotificationFieldValues: PropTypes.func.isRequired,
|
||||
saveNotification: PropTypes.func.isRequired,
|
||||
testNotification: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect } from 'react';
|
||||
import keyboardShortcuts from 'Components/keyboardShortcuts';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function PendingChangesModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
bindShortcut,
|
||||
unbindShortcut
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
bindShortcut('enter', onConfirm);
|
||||
|
||||
return () => unbindShortcut('enter', onConfirm);
|
||||
}
|
||||
}, [bindShortcut, unbindShortcut, isOpen, onConfirm]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onCancel}
|
||||
>
|
||||
<ModalContent onModalClose={onCancel}>
|
||||
<ModalHeader>{translate('UnsavedChanges')}</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{translate('PendingChangesMessage')}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
kind={kinds.DEFAULT}
|
||||
onPress={onCancel}
|
||||
>
|
||||
{translate('PendingChangesStayReview')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
autoFocus={true}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onConfirm}
|
||||
>
|
||||
{translate('PendingChangesDiscardChanges')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
PendingChangesModal.propTypes = {
|
||||
className: PropTypes.string,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
kind: PropTypes.oneOf(kinds.all),
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
bindShortcut: PropTypes.func.isRequired,
|
||||
unbindShortcut: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
PendingChangesModal.defaultProps = {
|
||||
kind: kinds.PRIMARY
|
||||
};
|
||||
|
||||
export default keyboardShortcuts(PendingChangesModal);
|
55
frontend/src/Settings/PendingChangesModal.tsx
Normal file
55
frontend/src/Settings/PendingChangesModal.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import useKeyboardShortcuts from 'Helpers/Hooks/useKeyboardShortcuts';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
interface PendingChangesModalProps {
|
||||
className?: string;
|
||||
isOpen: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
function PendingChangesModal({
|
||||
isOpen,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: PendingChangesModalProps) {
|
||||
const { bindShortcut, unbindShortcut } = useKeyboardShortcuts();
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
bindShortcut('acceptConfirmModal', onConfirm);
|
||||
}
|
||||
|
||||
return () => unbindShortcut('acceptConfirmModal');
|
||||
}, [bindShortcut, unbindShortcut, isOpen, onConfirm]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onModalClose={onCancel}>
|
||||
<ModalContent onModalClose={onCancel}>
|
||||
<ModalHeader>{translate('UnsavedChanges')}</ModalHeader>
|
||||
|
||||
<ModalBody>{translate('PendingChangesMessage')}</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button kind={kinds.DEFAULT} onPress={onCancel}>
|
||||
{translate('PendingChangesStayReview')}
|
||||
</Button>
|
||||
|
||||
<Button autoFocus={true} kind={kinds.DANGER} onPress={onConfirm}>
|
||||
{translate('PendingChangesDiscardChanges')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default PendingChangesModal;
|
|
@ -3,7 +3,7 @@ import { DndProvider } from 'react-dnd-multi-backend';
|
|||
import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import DelayProfilesConnector from './Delay/DelayProfilesConnector';
|
||||
import QualityProfilesConnector from './Quality/QualityProfilesConnector';
|
||||
|
@ -20,7 +20,7 @@ class Profiles extends Component {
|
|||
render() {
|
||||
return (
|
||||
<PageContent title={translate('Profiles')}>
|
||||
<SettingsToolbarConnector showSave={false} />
|
||||
<SettingsToolbar showSave={false} />
|
||||
|
||||
<PageContentBody>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
|
|
|
@ -5,7 +5,7 @@ import PageContentBody from 'Components/Page/PageContentBody';
|
|||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityDefinitionsConnector from './Definition/QualityDefinitionsConnector';
|
||||
import ResetQualityDefinitionsModal from './Reset/ResetQualityDefinitionsModal';
|
||||
|
@ -64,7 +64,7 @@ class Quality extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('QualitySettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
additionalButtons={
|
||||
|
|
|
@ -3,21 +3,16 @@ import Link from 'Components/Link/Link';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import SettingsToolbarConnector from './SettingsToolbarConnector';
|
||||
import SettingsToolbar from './SettingsToolbar';
|
||||
import styles from './Settings.css';
|
||||
|
||||
function Settings() {
|
||||
return (
|
||||
<PageContent title={translate('Settings')}>
|
||||
<SettingsToolbarConnector
|
||||
hasPendingChanges={false}
|
||||
/>
|
||||
<SettingsToolbar hasPendingChanges={false} />
|
||||
|
||||
<PageContentBody>
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/mediamanagement"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/mediamanagement">
|
||||
{translate('MediaManagement')}
|
||||
</Link>
|
||||
|
||||
|
@ -25,10 +20,7 @@ function Settings() {
|
|||
{translate('MediaManagementSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/profiles"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/profiles">
|
||||
{translate('Profiles')}
|
||||
</Link>
|
||||
|
||||
|
@ -36,10 +28,7 @@ function Settings() {
|
|||
{translate('ProfilesSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/quality"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/quality">
|
||||
{translate('Quality')}
|
||||
</Link>
|
||||
|
||||
|
@ -47,10 +36,7 @@ function Settings() {
|
|||
{translate('QualitySettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/customformats"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/customformats">
|
||||
{translate('CustomFormats')}
|
||||
</Link>
|
||||
|
||||
|
@ -58,10 +44,7 @@ function Settings() {
|
|||
{translate('CustomFormatsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/indexers"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/indexers">
|
||||
{translate('Indexers')}
|
||||
</Link>
|
||||
|
||||
|
@ -69,10 +52,7 @@ function Settings() {
|
|||
{translate('IndexersSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/downloadclients"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/downloadclients">
|
||||
{translate('DownloadClients')}
|
||||
</Link>
|
||||
|
||||
|
@ -80,10 +60,7 @@ function Settings() {
|
|||
{translate('DownloadClientsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/importlists"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/importlists">
|
||||
{translate('ImportLists')}
|
||||
</Link>
|
||||
|
||||
|
@ -91,10 +68,7 @@ function Settings() {
|
|||
{translate('ImportListsSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/connect"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/connect">
|
||||
{translate('Connect')}
|
||||
</Link>
|
||||
|
||||
|
@ -102,10 +76,7 @@ function Settings() {
|
|||
{translate('ConnectSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/metadata"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/metadata">
|
||||
{translate('Metadata')}
|
||||
</Link>
|
||||
|
||||
|
@ -113,10 +84,7 @@ function Settings() {
|
|||
{translate('MetadataSettingsSeriesSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/metadatasource"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/metadatasource">
|
||||
{translate('MetadataSource')}
|
||||
</Link>
|
||||
|
||||
|
@ -124,21 +92,13 @@ function Settings() {
|
|||
{translate('MetadataSourceSettingsSeriesSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/tags"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/tags">
|
||||
{translate('Tags')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
{translate('TagsSettingsSummary')}
|
||||
</div>
|
||||
<div className={styles.summary}>{translate('TagsSettingsSummary')}</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/general"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/general">
|
||||
{translate('General')}
|
||||
</Link>
|
||||
|
||||
|
@ -146,22 +106,14 @@ function Settings() {
|
|||
{translate('GeneralSettingsSummary')}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to="/settings/ui"
|
||||
>
|
||||
<Link className={styles.link} to="/settings/ui">
|
||||
{translate('Ui')}
|
||||
</Link>
|
||||
|
||||
<div className={styles.summary}>
|
||||
{translate('UiSettingsSummary')}
|
||||
</div>
|
||||
<div className={styles.summary}>{translate('UiSettingsSummary')}</div>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
Settings.propTypes = {
|
||||
};
|
||||
|
||||
export default Settings;
|
|
@ -1,106 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AdvancedSettingsButton from './AdvancedSettingsButton';
|
||||
import PendingChangesModal from './PendingChangesModal';
|
||||
|
||||
class SettingsToolbar extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.bindShortcut(shortcuts.SAVE_SETTINGS.key, this.saveSettings, { isGlobal: true });
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
saveSettings = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const {
|
||||
hasPendingChanges,
|
||||
onSavePress
|
||||
} = this.props;
|
||||
|
||||
if (hasPendingChanges) {
|
||||
onSavePress();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
advancedSettings,
|
||||
showSave,
|
||||
isSaving,
|
||||
hasPendingChanges,
|
||||
hasPendingLocation,
|
||||
additionalButtons,
|
||||
onSavePress,
|
||||
onConfirmNavigation,
|
||||
onCancelNavigation,
|
||||
onAdvancedSettingsPress
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
/>
|
||||
|
||||
{
|
||||
showSave &&
|
||||
<PageToolbarButton
|
||||
label={hasPendingChanges ? translate('SaveChanges') : translate('NoChanges')}
|
||||
iconName={icons.SAVE}
|
||||
isSpinning={isSaving}
|
||||
isDisabled={!hasPendingChanges}
|
||||
onPress={onSavePress}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
additionalButtons
|
||||
}
|
||||
</PageToolbarSection>
|
||||
|
||||
<PendingChangesModal
|
||||
isOpen={hasPendingLocation}
|
||||
onConfirm={onConfirmNavigation}
|
||||
onCancel={onCancelNavigation}
|
||||
/>
|
||||
</PageToolbar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SettingsToolbar.propTypes = {
|
||||
advancedSettings: PropTypes.bool.isRequired,
|
||||
showSave: PropTypes.bool.isRequired,
|
||||
isSaving: PropTypes.bool,
|
||||
hasPendingLocation: PropTypes.bool.isRequired,
|
||||
hasPendingChanges: PropTypes.bool,
|
||||
additionalButtons: PropTypes.node,
|
||||
onSavePress: PropTypes.func,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onConfirmNavigation: PropTypes.func.isRequired,
|
||||
onCancelNavigation: PropTypes.func.isRequired,
|
||||
bindShortcut: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
SettingsToolbar.defaultProps = {
|
||||
showSave: true
|
||||
};
|
||||
|
||||
export default keyboardShortcuts(SettingsToolbar);
|
149
frontend/src/Settings/SettingsToolbar.tsx
Normal file
149
frontend/src/Settings/SettingsToolbar.tsx
Normal file
|
@ -0,0 +1,149 @@
|
|||
import { Action, Location, UnregisterCallback } from 'history';
|
||||
import React, {
|
||||
ReactElement,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import useKeyboardShortcuts from 'Helpers/Hooks/useKeyboardShortcuts';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AdvancedSettingsButton from './AdvancedSettingsButton';
|
||||
import PendingChangesModal from './PendingChangesModal';
|
||||
|
||||
interface SettingsToolbarProps {
|
||||
showSave?: boolean;
|
||||
isSaving?: boolean;
|
||||
hasPendingChanges?: boolean;
|
||||
// TODO: This should do type checking like PageToolbarSectionProps,
|
||||
// but this works for the time being.
|
||||
additionalButtons?: ReactElement | null;
|
||||
onSavePress?: () => void;
|
||||
}
|
||||
|
||||
function SettingsToolbar({
|
||||
showSave = true,
|
||||
isSaving,
|
||||
hasPendingChanges,
|
||||
additionalButtons = null,
|
||||
onSavePress,
|
||||
}: SettingsToolbarProps) {
|
||||
const { bindShortcut, unbindShortcut } = useKeyboardShortcuts();
|
||||
const history = useHistory();
|
||||
const [nextLocation, setNextLocation] = useState<Location | null>(null);
|
||||
const [nextLocationAction, setNextLocationAction] = useState<Action | null>(
|
||||
null
|
||||
);
|
||||
const hasConfirmed = useRef(false);
|
||||
const unblocker = useRef<UnregisterCallback>();
|
||||
|
||||
const handleConfirmNavigation = useCallback(() => {
|
||||
if (!nextLocation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const path = `${nextLocation.pathname}${nextLocation.search}`;
|
||||
|
||||
hasConfirmed.current = true;
|
||||
|
||||
if (nextLocationAction === 'PUSH') {
|
||||
history.push(path);
|
||||
} else {
|
||||
// Unfortunately back and forward both use POP,
|
||||
// which means we don't actually know which direction
|
||||
// the user wanted to go, assuming back.
|
||||
|
||||
history.goBack();
|
||||
}
|
||||
}, [nextLocation, nextLocationAction, history]);
|
||||
|
||||
const handleCancelNavigation = useCallback(() => {
|
||||
setNextLocation(null);
|
||||
setNextLocationAction(null);
|
||||
hasConfirmed.current = false;
|
||||
}, []);
|
||||
|
||||
const handleRouterLeaving = useCallback(
|
||||
(routerLocation: Location, routerAction: Action) => {
|
||||
if (hasConfirmed.current) {
|
||||
setNextLocation(null);
|
||||
setNextLocationAction(null);
|
||||
hasConfirmed.current = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPendingChanges) {
|
||||
setNextLocation(routerLocation);
|
||||
setNextLocationAction(routerAction);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
[hasPendingChanges]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
unblocker.current = history.block(handleRouterLeaving);
|
||||
|
||||
return () => {
|
||||
unblocker.current?.();
|
||||
};
|
||||
}, [history, handleRouterLeaving]);
|
||||
|
||||
useEffect(() => {
|
||||
bindShortcut(
|
||||
'saveSettings',
|
||||
() => {
|
||||
if (hasPendingChanges) {
|
||||
onSavePress?.();
|
||||
}
|
||||
},
|
||||
{
|
||||
isGlobal: true,
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unbindShortcut('saveSettings');
|
||||
};
|
||||
}, [hasPendingChanges, bindShortcut, unbindShortcut, onSavePress]);
|
||||
|
||||
return (
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<AdvancedSettingsButton showLabel={true} />
|
||||
{showSave ? (
|
||||
<PageToolbarButton
|
||||
label={
|
||||
hasPendingChanges
|
||||
? translate('SaveChanges')
|
||||
: translate('NoChanges')
|
||||
}
|
||||
iconName={icons.SAVE}
|
||||
isSpinning={isSaving}
|
||||
isDisabled={!hasPendingChanges}
|
||||
onPress={onSavePress}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{additionalButtons}
|
||||
</PageToolbarSection>
|
||||
|
||||
<PendingChangesModal
|
||||
isOpen={nextLocation !== null}
|
||||
onConfirm={handleConfirmNavigation}
|
||||
onCancel={handleCancelNavigation}
|
||||
/>
|
||||
</PageToolbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsToolbar;
|
|
@ -1,148 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { toggleAdvancedSettings } from 'Store/Actions/settingsActions';
|
||||
import SettingsToolbar from './SettingsToolbar';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
advancedSettings: state.settings.advancedSettings
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleAdvancedSettings
|
||||
};
|
||||
|
||||
class SettingsToolbarConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
nextLocation: null,
|
||||
nextLocationAction: null,
|
||||
confirmed: false
|
||||
};
|
||||
|
||||
this._unblock = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._unblock = this.props.history.block(this.routerWillLeave);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._unblock) {
|
||||
this._unblock();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
routerWillLeave = (nextLocation, nextLocationAction) => {
|
||||
if (this.state.confirmed) {
|
||||
this.setState({
|
||||
nextLocation: null,
|
||||
nextLocationAction: null,
|
||||
confirmed: false
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.props.hasPendingChanges ) {
|
||||
this.setState({
|
||||
nextLocation,
|
||||
nextLocationAction
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
onConfirmNavigation = () => {
|
||||
const {
|
||||
nextLocation,
|
||||
nextLocationAction
|
||||
} = this.state;
|
||||
|
||||
const history = this.props.history;
|
||||
|
||||
const path = `${nextLocation.pathname}${nextLocation.search}`;
|
||||
|
||||
this.setState({
|
||||
confirmed: true
|
||||
}, () => {
|
||||
if (nextLocationAction === 'PUSH') {
|
||||
history.push(path);
|
||||
} else {
|
||||
// Unfortunately back and forward both use POP,
|
||||
// which means we don't actually know which direction
|
||||
// the user wanted to go, assuming back.
|
||||
|
||||
history.goBack();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onCancelNavigation = () => {
|
||||
this.setState({
|
||||
nextLocation: null,
|
||||
nextLocationAction: null,
|
||||
confirmed: false
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const hasPendingLocation = this.state.nextLocation !== null;
|
||||
|
||||
return (
|
||||
<SettingsToolbar
|
||||
hasPendingLocation={hasPendingLocation}
|
||||
onSavePress={this.props.onSavePress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onConfirmNavigation={this.onConfirmNavigation}
|
||||
onCancelNavigation={this.onCancelNavigation}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const historyShape = {
|
||||
block: PropTypes.func.isRequired,
|
||||
goBack: PropTypes.func.isRequired,
|
||||
push: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
SettingsToolbarConnector.propTypes = {
|
||||
showSave: PropTypes.bool,
|
||||
hasPendingChanges: PropTypes.bool.isRequired,
|
||||
history: PropTypes.shape(historyShape).isRequired,
|
||||
onSavePress: PropTypes.func,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
SettingsToolbarConnector.defaultProps = {
|
||||
hasPendingChanges: false
|
||||
};
|
||||
|
||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(SettingsToolbarConnector));
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AutoTaggings from './AutoTagging/AutoTaggings';
|
||||
import TagsConnector from './TagsConnector';
|
||||
|
@ -9,7 +9,7 @@ import TagsConnector from './TagsConnector';
|
|||
function TagSettings() {
|
||||
return (
|
||||
<PageContent title={translate('Tags')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
showSave={false}
|
||||
/>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import themes from 'Styles/Themes';
|
||||
import titleCase from 'Utilities/String/titleCase';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
@ -78,7 +78,7 @@ class UISettings extends Component {
|
|||
|
||||
return (
|
||||
<PageContent title={translate('UiSettings')}>
|
||||
<SettingsToolbarConnector
|
||||
<SettingsToolbar
|
||||
{...otherProps}
|
||||
onSavePress={onSavePress}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue