From e6448dd851656a19ce6803c6c78835b4dd3f0996 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Wed, 20 Jan 2021 22:41:39 +0800 Subject: [PATCH] SettingsModal: migrate to Functional Component --- .../modals/settings-modal/AboutTab.tsx | 4 +- .../modals/settings-modal/BandwidthTab.tsx | 201 ++++++++------- .../modals/settings-modal/ConnectivityTab.tsx | 238 ++++++++++-------- .../modals/settings-modal/ResourcesTab.tsx | 118 +++++---- .../modals/settings-modal/SettingsTab.tsx | 57 ----- .../modals/settings-modal/SettingsUtils.tsx | 31 +++ .../modals/settings-modal/UITab.tsx | 211 +++++++--------- 7 files changed, 429 insertions(+), 431 deletions(-) delete mode 100644 client/src/javascript/components/modals/settings-modal/SettingsTab.tsx create mode 100644 client/src/javascript/components/modals/settings-modal/SettingsUtils.tsx diff --git a/client/src/javascript/components/modals/settings-modal/AboutTab.tsx b/client/src/javascript/components/modals/settings-modal/AboutTab.tsx index 243b3c21..cdd9ffd1 100644 --- a/client/src/javascript/components/modals/settings-modal/AboutTab.tsx +++ b/client/src/javascript/components/modals/settings-modal/AboutTab.tsx @@ -1,4 +1,4 @@ -import {lazy, Suspense} from 'react'; +import {FC, lazy, Suspense} from 'react'; import packageJSON from '../../../../../../package.json'; @@ -8,7 +8,7 @@ const AboutMarkdown = lazy(() => const FLOOD_PROJECT_URL = 'https://github.com/jesec/flood'; -const AboutTab = () => ( +const AboutTab: FC = () => ( packageJSON.version} diff --git a/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx b/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx index 8bfdcfe6..c123783f 100644 --- a/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx +++ b/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx @@ -1,11 +1,14 @@ import {FormattedMessage} from 'react-intl'; -import {FormEvent} from 'react'; +import {FC, FormEvent, useState} from 'react'; import {Form, FormRow, Textbox} from '@client/ui'; import SettingStore from '@client/stores/SettingStore'; +import {FloodSettings} from '@shared/types/FloodSettings'; +import {ClientSettings} from '@shared/types/ClientSettings'; + +import {getChangedClientSetting, handleClientSettingChange} from './SettingsUtils'; import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; const processSpeedsForDisplay = (speeds: number[]): string | undefined => { if (!speeds || speeds.length === 0) { @@ -26,98 +29,104 @@ const processSpeedsForSave = (speeds = ''): number[] => { .map((speed) => Number(speed)); }; -export default class BandwidthTab extends SettingsTab { - handleFormChange = ({ - event, - formData, - }: { - event: Event | FormEvent; - formData: Record; - }) => { - const inputElement = event.target as HTMLInputElement; - - if (inputElement.name === 'dropdownPresetDownload' || inputElement.name === 'dropdownPresetUpload') { - this.props.onSettingsChange({ - speedLimits: { - download: processSpeedsForSave(formData.dropdownPresetDownload as string), - upload: processSpeedsForSave(formData.dropdownPresetUpload as string), - }, - }); - - return; - } - - this.handleClientSettingChange(event); - }; - - render() { - return ( -
- - - - - } - id="dropdownPresetDownload" - /> - - - } - id="dropdownPresetUpload" - /> - - - } - id="throttleGlobalDownSpeed" - /> - } - id="throttleGlobalUpSpeed" - /> - - - - - - } - id="throttleMaxUploads" - /> - } - id="throttleMaxUploadsGlobal" - /> - - - } - id="throttleMaxDownloads" - /> - } - id="throttleMaxDownloadsGlobal" - /> - -
- ); - } +interface BandwidthTabProps { + onSettingsChange: (changeSettings: Partial) => void; + onClientSettingsChange: (changeSettings: Partial) => void; } + +const BandwidthTab: FC = ({onSettingsChange, onClientSettingsChange}: BandwidthTabProps) => { + const [changedClientSettings, setChangedClientSettings] = useState>({}); + + return ( +
; formData: Record}) => { + const inputElement = event.target as HTMLInputElement; + + if (inputElement.name === 'dropdownPresetDownload' || inputElement.name === 'dropdownPresetUpload') { + onSettingsChange({ + speedLimits: { + download: processSpeedsForSave(formData.dropdownPresetDownload as string), + upload: processSpeedsForSave(formData.dropdownPresetUpload as string), + }, + }); + + return; + } + + const newChangedClientSettings = { + ...changedClientSettings, + ...handleClientSettingChange(event), + }; + + setChangedClientSettings(newChangedClientSettings); + onClientSettingsChange(newChangedClientSettings); + }}> + + + + + } + id="dropdownPresetDownload" + /> + + + } + id="dropdownPresetUpload" + /> + + + } + id="throttleGlobalDownSpeed" + /> + } + id="throttleGlobalUpSpeed" + /> + + + + + + } + id="throttleMaxUploads" + /> + } + id="throttleMaxUploadsGlobal" + /> + + + } + id="throttleMaxDownloads" + /> + } + id="throttleMaxDownloadsGlobal" + /> + + + ); +}; + +export default BandwidthTab; diff --git a/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx b/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx index 84b02ab4..e993a83d 100644 --- a/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx +++ b/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx @@ -1,116 +1,134 @@ +import {FC, useState} from 'react'; import {FormattedMessage} from 'react-intl'; import {Checkbox, Form, FormRow, Textbox} from '@client/ui'; -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; +import {ClientSettings} from '@shared/types/ClientSettings'; -export default class ConnectivityTab extends SettingsTab { - render() { - return ( -
this.handleClientSettingChange(event)}> - - - - - } - width="one-quarter" - /> - - - - - - - - - } - /> - } - /> - - - - - - } - width="one-quarter" - /> - - - - - - - - - - - - } - /> - } - /> - - - } - /> - } - /> - - - } - width="one-half" - /> - -
- ); - } +import {getChangedClientSetting, handleClientSettingChange} from './SettingsUtils'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; + +interface ConnectivityTabProps { + onClientSettingsChange: (changeSettings: Partial) => void; } + +const ConnectivityTab: FC = ({onClientSettingsChange}: ConnectivityTabProps) => { + const [changedClientSettings, setChangedClientSettings] = useState>({}); + + return ( +
{ + const newChangedClientSettings = { + ...changedClientSettings, + ...handleClientSettingChange(event), + }; + + setChangedClientSettings(newChangedClientSettings); + onClientSettingsChange(newChangedClientSettings); + }}> + + + + + } + width="one-quarter" + /> + + + + + + + + + } + /> + } + /> + + + + + + } + width="one-quarter" + /> + + + + + + + + + + + + } + /> + } + /> + + + } + /> + } + /> + + + } + width="one-half" + /> + +
+ ); +}; + +export default ConnectivityTab; diff --git a/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx b/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx index 36e351cb..23142e62 100644 --- a/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx +++ b/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx @@ -1,56 +1,74 @@ +import {FC, useState} from 'react'; import {FormattedMessage} from 'react-intl'; import {Checkbox, Form, FormRow, Textbox} from '@client/ui'; -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; +import {ClientSettings} from '@shared/types/ClientSettings'; -export default class ResourcesTab extends SettingsTab { - render() { - return ( -
this.handleClientSettingChange(event)}> - - - - - } - /> - - - } - width="one-half" - /> - - - - - - - - - - (MB) - - } - width="one-half" - /> - -
- ); - } +import {getChangedClientSetting, handleClientSettingChange} from './SettingsUtils'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; + +interface ResourcesTabProps { + onClientSettingsChange: (changeSettings: Partial) => void; } + +const ResourcesTab: FC = ({onClientSettingsChange}: ResourcesTabProps) => { + const [changedClientSettings, setChangedClientSettings] = useState>({}); + + return ( +
{ + const newChangedClientSettings = { + ...changedClientSettings, + ...handleClientSettingChange(event), + }; + + setChangedClientSettings(newChangedClientSettings); + onClientSettingsChange(newChangedClientSettings); + }}> + + + + + } + /> + + + } + width="one-half" + /> + + + + + + + + + + (MB) + + } + width="one-half" + /> + +
+ ); +}; + +export default ResourcesTab; diff --git a/client/src/javascript/components/modals/settings-modal/SettingsTab.tsx b/client/src/javascript/components/modals/settings-modal/SettingsTab.tsx deleted file mode 100644 index 12cbc69e..00000000 --- a/client/src/javascript/components/modals/settings-modal/SettingsTab.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import {Component, FormEvent} from 'react'; -import {WrappedComponentProps} from 'react-intl'; - -import type {ClientSetting, ClientSettings} from '@shared/types/ClientSettings'; -import type {FloodSettings} from '@shared/types/FloodSettings'; - -import SettingStore from '../../../stores/SettingStore'; - -interface SettingsTabProps extends WrappedComponentProps { - onSettingsChange: (changeSettings: Partial) => void; - onClientSettingsChange: (changeSettings: Partial) => void; -} - -interface SettingsTabStates { - changedClientSettings: Partial; -} - -class SettingsTab extends Component { - constructor(props: SettingsTabProps) { - super(props); - - this.state = { - changedClientSettings: {}, - }; - } - - getChangedClientSetting(property: T): ClientSettings[T] | undefined { - if (this.state.changedClientSettings[property] != null) { - return this.state.changedClientSettings[property] as ClientSettings[T]; - } - - return SettingStore.clientSettings?.[property]; - } - - handleClientSettingChange(event: FormEvent | Event) { - const inputElement = event.target as HTMLInputElement; - const property = inputElement.name as ClientSetting; - const {value, type, checked} = inputElement; - - let changedClientSetting: Partial = {}; - if (type === 'checkbox') { - changedClientSetting = {[property]: checked}; - } else { - changedClientSetting = {[property]: value}; - } - - this.setState((prev) => ({ - changedClientSettings: { - ...prev.changedClientSettings, - changedClientSetting, - }, - })); - this.props.onClientSettingsChange(changedClientSetting); - } -} - -export default SettingsTab; diff --git a/client/src/javascript/components/modals/settings-modal/SettingsUtils.tsx b/client/src/javascript/components/modals/settings-modal/SettingsUtils.tsx new file mode 100644 index 00000000..fee621a0 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/SettingsUtils.tsx @@ -0,0 +1,31 @@ +import {FormEvent} from 'react'; + +import type {ClientSetting, ClientSettings} from '@shared/types/ClientSettings'; + +import SettingStore from '../../../stores/SettingStore'; + +export const getChangedClientSetting = ( + changedClientSettings: Partial, + property: T, +): ClientSettings[T] | undefined => { + if (changedClientSettings[property] != null) { + return changedClientSettings[property] as ClientSettings[T]; + } + + return SettingStore.clientSettings?.[property]; +}; + +export const handleClientSettingChange = (event: FormEvent | Event): Partial => { + const inputElement = event.target as HTMLInputElement; + const property = inputElement.name as ClientSetting; + const {value, type, checked} = inputElement; + + let changedClientSetting: Partial = {}; + if (type === 'checkbox') { + changedClientSetting = {[property]: checked}; + } else { + changedClientSetting = {[property]: value}; + } + + return changedClientSetting; +}; diff --git a/client/src/javascript/components/modals/settings-modal/UITab.tsx b/client/src/javascript/components/modals/settings-modal/UITab.tsx index eecb5649..c0167975 100644 --- a/client/src/javascript/components/modals/settings-modal/UITab.tsx +++ b/client/src/javascript/components/modals/settings-modal/UITab.tsx @@ -1,5 +1,5 @@ -import {FormattedMessage, injectIntl} from 'react-intl'; -import {FormEvent} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; +import {FC, useState} from 'react'; import {Form, FormRow, Select, SelectItem, Radio} from '@client/ui'; import Languages from '@client/constants/Languages'; @@ -10,122 +10,101 @@ import type {Language} from '@client/constants/Languages'; import type {FloodSettings} from '@shared/types/FloodSettings'; import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; import TorrentContextMenuActionsList from './lists/TorrentContextMenuActionsList'; import TorrentListColumnsList from './lists/TorrentListColumnsList'; -class UITab extends SettingsTab { - torrentListViewSize = SettingStore.floodSettings.torrentListViewSize; - selectedLanguage = SettingStore.floodSettings.language; - UITagSelectorMode = SettingStore.floodSettings.UITagSelectorMode; - - getLanguageSelectOptions() { - return Object.keys(Languages).map((languageID) => ( - - {Languages[languageID as 'auto'].id != null - ? this.props.intl.formatMessage({ - id: Languages[languageID as 'auto'].id, - }) - : Languages[languageID as Language]} - - )); - } - - handleFormChange = ({ - event, - formData, - }: { - event: Event | FormEvent; - formData: Record; - }) => { - const inputElement = event.target as HTMLInputElement; - - if (inputElement.type === 'radio') { - this.torrentListViewSize = formData['ui-torrent-size'] as FloodSettings['torrentListViewSize']; - this.UITagSelectorMode = formData['ui-tag-selector-mode'] as FloodSettings['UITagSelectorMode']; - this.props.onSettingsChange({ - torrentListViewSize: this.torrentListViewSize, - UITagSelectorMode: this.UITagSelectorMode, - }); - } - - if (inputElement.name === 'language') { - this.selectedLanguage = formData.language as FloodSettings['language']; - if (this.selectedLanguage === 'translate') { - SettingStore.saveFloodSettings({language: 'translate'}); - } else { - this.props.onSettingsChange({language: this.selectedLanguage}); - } - } - }; - - render() { - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); - } +interface UITabProps { + onSettingsChange: (changeSettings: Partial) => void; } -export default injectIntl(UITab); +const UITab: FC = ({onSettingsChange}: UITabProps) => { + const intl = useIntl(); + const [torrentListViewSize, setTorrentListViewSize] = useState(SettingStore.floodSettings.torrentListViewSize); + const [selectedLanguage, setSelectedLanguage] = useState(SettingStore.floodSettings.language); + const [UITagSelectorMode, setUITagSelectorMode] = useState(SettingStore.floodSettings.UITagSelectorMode); + + return ( +
{ + const inputElement = event.target as HTMLInputElement; + + if (inputElement.type === 'radio') { + setTorrentListViewSize(formData['ui-torrent-size'] as FloodSettings['torrentListViewSize']); + setUITagSelectorMode(formData['ui-tag-selector-mode'] as FloodSettings['UITagSelectorMode']); + onSettingsChange({ + torrentListViewSize, + UITagSelectorMode, + }); + } + + if (inputElement.name === 'language') { + const newSelectedLanguage = formData.language as FloodSettings['language']; + if (newSelectedLanguage === 'translate') { + SettingStore.saveFloodSettings({language: 'translate'}); + } else { + setSelectedLanguage(newSelectedLanguage); + onSettingsChange({ + language: selectedLanguage, + }); + } + } + }}> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +export default UITab;