Convert SettingsToolbar to TypeScript

pull/7605/head
Mark McDowall 2 months ago
parent 64160866c3
commit 86c785ffa0
No known key found for this signature in database

@ -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;

@ -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);

@ -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);

@ -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…
Cancel
Save