Add settings modal with speed limit presets

This commit is contained in:
John Furrow
2016-04-11 21:35:51 -07:00
parent 0ab52287f8
commit 65fa3e5262
19 changed files with 614 additions and 26 deletions
@@ -41,7 +41,10 @@ export default class Modal extends React.Component {
let content = this.props.content;
let footer = null;
let contentClasses = classnames('modal__content__wrapper',
`modal--align-${this.props.alignment}`);
`modal--align-${this.props.alignment}`, {
'modal--orientation--horizontal': this.props.orientation === 'horizontal',
'modal--orientation--vertical': this.props.orientation === 'vertical'
}, this.props.classNames);
let headerClasses = classnames('modal__header', {
'has-tabs': this.props.tabs
});
@@ -58,7 +61,10 @@ export default class Modal extends React.Component {
}
if (this.props.actions) {
footer = <ModalActions actions={this.props.actions} dismiss={this.props.dismiss} />;
footer = (
<ModalActions actions={this.props.actions}
dismiss={this.props.dismiss} />
);
}
return (
@@ -79,5 +85,7 @@ export default class Modal extends React.Component {
}
Modal.defaultProps = {
alignment: 'left'
alignment: 'left',
classNames: null,
orientation: 'horizontal'
};
@@ -7,6 +7,7 @@ import ConfirmModal from './ConfirmModal';
import EventTypes from '../../constants/EventTypes';
import Modal from './Modal';
import MoveTorrents from './MoveTorrents';
import SettingsModal from './SettingsModal';
import UIActions from '../../actions/UIActions';
import UIStore from '../../stores/UIStore';
@@ -23,7 +24,8 @@ export default class Modals extends React.Component {
this.modals = {
confirm: ConfirmModal,
'move-torrents': MoveTorrents,
'add-torrents': AddTorrents
'add-torrents': AddTorrents,
'settings': SettingsModal
};
this.state = {
@@ -0,0 +1,120 @@
import classnames from 'classnames';
import React from 'react';
import EventTypes from '../../constants/EventTypes';
import Modal from './Modal';
import SettingsSpeedLimit from './SettingsSpeedLimit';
import SettingsStore from '../../stores/SettingsStore';
const METHODS_TO_BIND = [
'handleSettingsChange',
'handleSaveSettingsClick',
'handleSettingsFetchRequestSuccess'
];
export default class AddTorrents extends React.Component {
constructor() {
super();
this.state = {
settings: {
speedLimits: {
download: null,
upload: null
}
}
};
METHODS_TO_BIND.forEach((method) => {
this[method] = this[method].bind(this);
});
}
componentDidMount() {
SettingsStore.listen(EventTypes.SETTINGS_FETCH_REQUEST_SUCCESS, this.handleSettingsFetchRequestSuccess);
SettingsStore.fetchSettings(EventTypes.SETTINGS_FETCH_REQUEST_ERROR, this.handleSettingsFetchRequestError);
}
componentWillUnmount() {
SettingsStore.unlisten(EventTypes.SETTINGS_FETCH_REQUEST_SUCCESS, this.handleSettingsFetchRequestSuccess);
}
getActions() {
let icon = null;
let primaryButtonText = 'Add Torrent';
if (this.state.isAddingTorrents) {
icon = <LoadingIndicatorDots viewBox="0 0 32 32" />;
primaryButtonText = 'Adding...';
}
return [
{
clickHandler: null,
content: 'Cancel',
triggerDismiss: true,
type: 'secondary'
},
{
clickHandler: this.handleSaveSettingsClick,
content: 'Save Settings',
triggerDismiss: false,
type: 'primary'
}
];
}
handleSaveSettingsClick() {
SettingsStore.saveSettings(this.state.settings);
}
handleSettingsFetchRequestError() {
console.log(error);
}
handleSettingsFetchRequestSuccess() {
this.setState({
settings: SettingsStore.getSettings()
});
}
handleSettingsChange(changedSettings) {
let settings = this.mergeObjects(this.state.settings, changedSettings);
this.setState({settings});
}
mergeObjects(objA, objB) {
Object.keys(objB).forEach((key) => {
if (!objB.hasOwnProperty(key) || objB[key] == null) {
return;
}
// If it's an object, then recursive merge.
if (!Array.isArray(objB[key]) && !Array.isArray(objB[key]) && typeof objA[key] === 'object' && typeof objB[key] === 'object') {
objA[key] = this.mergeObjects(objA[key], objB[key]);
} else {
objA[key] = objB[key];
}
});
return objA;
}
render() {
let tabs = {
'speed-limit': {
content: (
<SettingsSpeedLimit onSettingsChange={this.handleSettingsChange}
settings={this.state.settings} />
),
label: 'Speed Limits'
}
};
return (
<Modal actions={this.getActions()} classNames="modal--large"
heading="Settings" orientation="vertical" dismiss={this.props.dismiss}
tabs={tabs} />
);
}
}
@@ -0,0 +1,129 @@
import React from 'react';
const METHODS_TO_BIND = ['handleDownloadTextChange', 'handleUploadTextChange'];
export default class AddTorrents extends React.Component {
constructor() {
super();
this.state = {
downloadValue: null,
uploadValue: null
}
METHODS_TO_BIND.forEach((method) => {
this[method] = this[method].bind(this);
});
}
arrayToString(array) {
return array.join(', ');
}
getTextboxValue(input = []) {
if (Array.isArray(input)) {
return this.arrayToString(input);
}
return input;
}
handleDownloadTextChange(event) {
this.setState({
downloadValue: event.target.value
});
this.props.onSettingsChange({
speedLimits: {
download: this.processSpeedsForSave(event.target.value)
}
});
}
handleUploadTextChange(event) {
this.setState({
uploadValue: event.target.value
});
this.props.onSettingsChange({
speedLimits: {
upload: this.processSpeedsForSave(event.target.value)
}
});
}
getDownloadValue() {
let displayedValue = this.state.downloadValue;
if (displayedValue == null) {
displayedValue = this.processSpeedsForDisplay(this.props.settings.speedLimits.download);
}
return displayedValue;
}
getUploadValue() {
let displayedValue = this.state.uploadValue;
if (displayedValue == null) {
displayedValue = this.processSpeedsForDisplay(this.props.settings.speedLimits.upload);
}
return displayedValue;
}
processSpeedsForDisplay(speeds = []) {
if (!speeds || speeds.length === 0) {
return;
}
return this.arrayToString(speeds.map((speed) => {
return Number(speed) / 1024;
}));
}
processSpeedsForSave(speeds = '') {
if (speeds === '') {
return [];
}
return this.stringToArray(speeds).map((speed) => {
return Number(speed) * 1024;
});
}
stringToArray(string = '') {
return string.replace(/\s/g, '').split(',');
}
render() {
let downloadValue = this.getDownloadValue();
let uploadValue = this.getUploadValue();
return (
<div>
<p className="modal__tab__introduction">
Provide a comma-separated list of speed values (in kilobytes per second). 0 represents unlimited.
</p>
<div className="form">
<div className="form__row">
<label className="form__label">
Download Presets
</label>
<input className="textbox" type="text"
onChange={this.handleDownloadTextChange}
value={downloadValue} />
</div>
<div className="form__row">
<label className="form__label">
Upload Presets
</label>
<input className="textbox" type="text"
onChange={this.handleUploadTextChange}
value={uploadValue} />
</div>
</div>
</div>
);
}
}