[Application configuration support] [Fix component unmount leak]
This commit is contained in:
287
src/containers/Configuration/Configuration.js
Normal file
287
src/containers/Configuration/Configuration.js
Normal file
@@ -0,0 +1,287 @@
|
||||
import React, {Component} from 'react';
|
||||
import styles from './Configuration.css';
|
||||
import Box from '../../components/UI/Box/Box';
|
||||
import Button from '../../components/UI/Button/Button';
|
||||
import ConfigurationItem from '../../components/ConfigurationItem/ConfigurationItem';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import Modal from '../../components/UI/Modal/Modal';
|
||||
|
||||
let ipcRenderer = null;
|
||||
if (!process.versions.hasOwnProperty('electron')) {
|
||||
ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null);
|
||||
}
|
||||
|
||||
class Configuration extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.on('get_config_template_reply', this.onGetConfigTemplateReply);
|
||||
ipcRenderer.on('get_config_reply', this.onGetConfigReply);
|
||||
ipcRenderer.on('set_config_values_reply', this.onSetConfigValuesReply);
|
||||
|
||||
ipcRenderer.send('get_config_template', {
|
||||
Directory: this.props.directory,
|
||||
StorageType: this.props.storageType,
|
||||
Version: this.props.version,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
state = {
|
||||
ChangedItems: [],
|
||||
ChangedObjectLookup: null,
|
||||
ObjectLookup: {},
|
||||
OriginalItemList: [],
|
||||
OriginalObjectLookup: {},
|
||||
ItemList: [],
|
||||
Saving: false,
|
||||
ShowAdvanced: false,
|
||||
Template: {}
|
||||
};
|
||||
|
||||
checkSaveRequired = () => {
|
||||
const changedItems = [];
|
||||
let i = 0;
|
||||
for (const item of this.state.ItemList) {
|
||||
if (this.state.OriginalItemList[i++].value !== item.value) {
|
||||
changedItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
let changedObjectLookup = null;
|
||||
for (const key of Object.keys(this.state.ObjectLookup)) {
|
||||
const changedObjectItems = [];
|
||||
let j = 0;
|
||||
for (const item of this.state.ObjectLookup[key]) {
|
||||
if (this.state.OriginalObjectLookup[key][j++].value !== item.value) {
|
||||
changedObjectItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (changedObjectItems.length > 0) {
|
||||
if (changedObjectLookup === null) {
|
||||
changedObjectLookup = {};
|
||||
}
|
||||
changedObjectLookup[key] = changedObjectItems;
|
||||
}
|
||||
}
|
||||
|
||||
if ((changedItems.length > 0) || changedObjectLookup) {
|
||||
this.setState({
|
||||
ChangedItems: changedItems,
|
||||
ChangedObjectLookup: changedObjectLookup,
|
||||
});
|
||||
} else {
|
||||
this.props.closed();
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.removeListener('get_config_reply', this.onGetConfigReply);
|
||||
ipcRenderer.removeListener('get_config_template_reply', this.onGetConfigTemplateReply);
|
||||
ipcRenderer.removeListener('set_config_values', this.onSetConfigValuesReply);
|
||||
}
|
||||
};
|
||||
|
||||
createItemList = (config, template) => {
|
||||
const objectList = [];
|
||||
const itemList = Object
|
||||
.keys(config)
|
||||
.map(key => {
|
||||
return {
|
||||
advanced: template[key] ? template[key].advanced : false,
|
||||
label: key,
|
||||
value: config[key],
|
||||
};
|
||||
})
|
||||
.filter(i=> {
|
||||
let ret = template[i.label];
|
||||
if (ret && (template[i.label].type === 'object')) {
|
||||
objectList.push(i);
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
return {
|
||||
ObjectList: objectList,
|
||||
ItemList: itemList,
|
||||
}
|
||||
};
|
||||
|
||||
handleItemChanged = (target, idx) => {
|
||||
const itemList = [
|
||||
...this.state.ItemList
|
||||
];
|
||||
itemList[idx].value = target.value.toString();
|
||||
this.setState({
|
||||
ItemList: itemList
|
||||
});
|
||||
};
|
||||
|
||||
handleObjectItemChanged = (target, name, idx) => {
|
||||
const itemList = [
|
||||
...this.state.ObjectLookup[name]
|
||||
];
|
||||
const objectLookup = {
|
||||
...this.state.ObjectLookup,
|
||||
};
|
||||
|
||||
itemList[idx].value = target.value.toString();
|
||||
objectLookup[name] = itemList;
|
||||
this.setState({
|
||||
ObjectLookup: objectLookup,
|
||||
});
|
||||
};
|
||||
|
||||
onGetConfigReply = (event, arg) => {
|
||||
if (arg.data.Success) {
|
||||
const list = this.createItemList(arg.data.Config, this.state.Template);
|
||||
const itemListCopy = JSON.parse(JSON.stringify(list.ItemList));
|
||||
|
||||
let objectLookup = {};
|
||||
for (const obj of list.ObjectList) {
|
||||
const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template);
|
||||
objectLookup[obj.label] = list2.ItemList;
|
||||
}
|
||||
const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
|
||||
|
||||
this.setState({
|
||||
ItemList: list.ItemList,
|
||||
ObjectLookup: objectLookup,
|
||||
OriginalItemList: itemListCopy,
|
||||
OriginalObjectLookup: objectLookupCopy,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onGetConfigTemplateReply = (event, arg) => {
|
||||
if (arg.data.Success) {
|
||||
this.setState({
|
||||
Template: arg.data.Template,
|
||||
});
|
||||
ipcRenderer.send('get_config', {
|
||||
Directory: this.props.directory,
|
||||
StorageType: this.props.storageType,
|
||||
Version: this.props.version,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSetConfigValuesReply = () => {
|
||||
this.props.closed();
|
||||
};
|
||||
|
||||
saveAndClose = () => {
|
||||
if (ipcRenderer) {
|
||||
this.setState({
|
||||
Saving: true,
|
||||
});
|
||||
|
||||
const changedItems = [];
|
||||
for (const item of this.state.ChangedItems) {
|
||||
changedItems.push({
|
||||
Name: item.label,
|
||||
Value: item.value,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.state.ChangedObjectLookup) {
|
||||
for (const key of Object.keys(this.state.ChangedObjectLookup)) {
|
||||
for (const item of this.state.ChangedObjectLookup[key]) {
|
||||
changedItems.push({
|
||||
Name: key + '.' + item.label,
|
||||
Value: item.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ipcRenderer.send('set_config_values', {
|
||||
Directory: this.props.directory,
|
||||
Items: changedItems,
|
||||
StorageType: this.props.storageType,
|
||||
Version: this.props.version,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let confirmSave = null;
|
||||
if ((this.state.ChangedItems.length > 0) || this.state.ChangedObjectLookup) {
|
||||
confirmSave = (
|
||||
<Modal>
|
||||
<Box dxStyle={{width: '40vw', padding: '4px'}}>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1>
|
||||
<table width='100%'><tbody>
|
||||
<tr>
|
||||
<td align='center' width='50%'><Button clicked={this.saveAndClose} disabled={this.state.Saving} >Yes</Button></td>
|
||||
<td align='center' width='50%'><Button clicked={this.props.closed} disabled={this.state.Saving}>No</Button></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
let configurationItems = this.state.ItemList
|
||||
.map((k, i) => {
|
||||
return (
|
||||
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
|
||||
<ConfigurationItem advanced={k.advanced}
|
||||
changed={e=>this.handleItemChanged(e, i)}
|
||||
items={this.state.Template[k.label].items}
|
||||
key={i}
|
||||
label={k.label}
|
||||
template={this.state.Template[k.label]}
|
||||
value={k.value}/> :
|
||||
null)
|
||||
});
|
||||
|
||||
let objectItems = [];
|
||||
for (const key of Object.keys(this.state.ObjectLookup)) {
|
||||
objectItems.push((
|
||||
<div key={key}>
|
||||
<h1>{key}</h1>
|
||||
<div>
|
||||
{
|
||||
this.state.ObjectLookup[key].map((k, i) => {
|
||||
return (
|
||||
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
|
||||
<ConfigurationItem advanced={k.advanced}
|
||||
changed={e=>this.handleObjectItemChanged(e, key, i)}
|
||||
items={this.state.Template[key].template[k.label].items}
|
||||
key={i}
|
||||
label={k.label}
|
||||
template={this.state.Template[key].template[k.label]}
|
||||
value={k.value}/> :
|
||||
null)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<div styleName='Configuration'>
|
||||
{confirmSave}
|
||||
<Box dxDark dxStyle={{padding: '8px'}}>
|
||||
<div style={{float: 'right', margin: 0, padding: 0, marginTop: '-4px', boxSizing: 'border-box', display: 'block'}}>
|
||||
<b style={{cursor: 'pointer'}}
|
||||
onClick={this.checkSaveRequired}>X</b>
|
||||
</div>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>{this.props.storageType + ' Configuration'}</h1>
|
||||
<div style={{overflowY: 'auto', height: '90%'}}>
|
||||
{objectItems}
|
||||
<h1>Settings</h1>
|
||||
{configurationItems}
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CSSModules(Configuration, styles, {allowMultiple: true});
|
||||
Reference in New Issue
Block a user