[Application configuration support] [Fix component unmount leak]
This commit is contained in:
94
electron.js
94
electron.js
@@ -275,7 +275,8 @@ ipcMain.on('extract_release', (event, data) => {
|
|||||||
helpers.mkDirByPathSync(destination);
|
helpers.mkDirByPathSync(destination);
|
||||||
|
|
||||||
const stream = fs.createReadStream(data.Source);
|
const stream = fs.createReadStream(data.Source);
|
||||||
stream.pipe(unzip.Extract({ path: destination }))
|
stream
|
||||||
|
.pipe(unzip.Extract({ path: destination }))
|
||||||
.on('error', (e) => {
|
.on('error', (e) => {
|
||||||
try {
|
try {
|
||||||
helpers.removeDirectoryRecursively(destination);
|
helpers.removeDirectoryRecursively(destination);
|
||||||
@@ -301,6 +302,59 @@ ipcMain.on('extract_release', (event, data) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('get_config', (event, data) => {
|
||||||
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
|
helpers
|
||||||
|
.getConfig(dataDirectory, data.Version, data.StorageType)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.Code === 0) {
|
||||||
|
event.sender.send('get_config_reply', {
|
||||||
|
data: {
|
||||||
|
Success: true,
|
||||||
|
Config: data.Data,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.sender.send('get_config_reply', {
|
||||||
|
data: {
|
||||||
|
Error: data.Code,
|
||||||
|
Success: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e)=> {
|
||||||
|
event.sender.send('get_config_reply', {
|
||||||
|
data: {
|
||||||
|
Error: e,
|
||||||
|
Success: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('get_config_template', (event, data) => {
|
||||||
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
|
helpers
|
||||||
|
.getConfigTemplate(dataDirectory, data.Version, data.StorageType)
|
||||||
|
.then((data) => {
|
||||||
|
event.sender.send('get_config_template_reply', {
|
||||||
|
data: {
|
||||||
|
Success: true,
|
||||||
|
Template: data,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((e)=> {
|
||||||
|
event.sender.send('get_config_template_reply', {
|
||||||
|
data: {
|
||||||
|
Error: e,
|
||||||
|
Success: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.on('get_platform', (event) => {
|
ipcMain.on('get_platform', (event) => {
|
||||||
event.sender.send('get_platform_reply', {
|
event.sender.send('get_platform_reply', {
|
||||||
data: os.platform()
|
data: os.platform()
|
||||||
@@ -408,14 +462,34 @@ ipcMain.on('save_state', (event, data) => {
|
|||||||
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
|
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('unmount_drive', (event, data) => {
|
ipcMain.on('set_config_values', (event, data) => {
|
||||||
helpers.stopProcessByPID(data.PID)
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
.then((pid)=> {
|
const setConfigValue = (i) => {
|
||||||
if (mountedPIDs.indexOf(pid) === -1) {
|
if (i < data.Items.length) {
|
||||||
event.sender.send('unmount_drive_reply');
|
helpers
|
||||||
|
.setConfigValue(data.Items[i].Name, data.Items[i].Value, dataDirectory, data.StorageType, data.Version)
|
||||||
|
.then(() => {
|
||||||
|
setConfigValue(++i);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setConfigValue(++i);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.sender.send('set_config_values_reply', {});
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
.catch((e) => {
|
setConfigValue(0);
|
||||||
console.log(e);
|
});
|
||||||
});
|
|
||||||
|
ipcMain.on('unmount_drive', (event, data) => {
|
||||||
|
helpers
|
||||||
|
.stopProcessByPID(data.PID)
|
||||||
|
.then((pid)=> {
|
||||||
|
if (mountedPIDs.indexOf(pid) === -1) {
|
||||||
|
event.sender.send('unmount_drive_reply');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
110
helpers.js
110
helpers.js
@@ -195,6 +195,85 @@ module.exports.executeMount = (directory, version, storageType, location, exitCa
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.getConfig = (directory, version, storageType) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const processOptions = {
|
||||||
|
detached: true,
|
||||||
|
shell: false,
|
||||||
|
windowsHide: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
||||||
|
const args = [];
|
||||||
|
args.push('-dc');
|
||||||
|
if (storageType.toLowerCase() === 'hyperspace') {
|
||||||
|
args.push('-hs');
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = new spawn(command, args, processOptions);
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
process.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdout.on('data', (d)=> {
|
||||||
|
result += d;
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', () => {
|
||||||
|
const lines = result
|
||||||
|
.replace(/\r\n/g, '\n')
|
||||||
|
.split('\n');
|
||||||
|
|
||||||
|
const code = parseInt(lines[0], 10);
|
||||||
|
if (code === 0) {
|
||||||
|
lines.shift();
|
||||||
|
resolve({
|
||||||
|
Code: code,
|
||||||
|
Data: JSON.parse(lines.join('\n')),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
process.unref();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.getConfigTemplate = (directory, version, storageType) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const processOptions = {
|
||||||
|
detached: true,
|
||||||
|
shell: false,
|
||||||
|
windowsHide: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
||||||
|
const args = [];
|
||||||
|
args.push('-gt');
|
||||||
|
if (storageType.toLowerCase() === 'hyperspace') {
|
||||||
|
args.push('-hs');
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = new spawn(command, args, processOptions);
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
process.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdout.on('data', (d)=> {
|
||||||
|
result += d;
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', () => {
|
||||||
|
resolve(JSON.parse(result));
|
||||||
|
});
|
||||||
|
process.unref();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.getMissingDependencies = dependencies => {
|
module.exports.getMissingDependencies = dependencies => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!dependencies || (dependencies.length === 0)) {
|
if (!dependencies || (dependencies.length === 0)) {
|
||||||
@@ -323,6 +402,37 @@ module.exports.resolvePath = str => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.setConfigValue = (name, value, directory, storageType, version) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const processOptions = {
|
||||||
|
detached: true,
|
||||||
|
shell: false,
|
||||||
|
windowsHide: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
||||||
|
const args = [];
|
||||||
|
args.push('-set');
|
||||||
|
args.push(name);
|
||||||
|
args.push(value);
|
||||||
|
if (storageType.toLowerCase() === 'hyperspace') {
|
||||||
|
args.push('-hs');
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = new spawn(command, args, processOptions);
|
||||||
|
|
||||||
|
process.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
process.unref();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.stopProcessByPID = pid => {
|
module.exports.stopProcessByPID = pid => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
|||||||
145
src/App.js
145
src/App.js
@@ -1,18 +1,20 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import CSSModules from 'react-css-modules';
|
|
||||||
import styles from './App.css';
|
|
||||||
import Box from './components/UI/Box/Box';
|
|
||||||
import DropDown from './components/UI/DropDown/DropDown';
|
|
||||||
import * as Constants from './constants';
|
import * as Constants from './constants';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import MountItems from './containers/MountItems/MountItems';
|
import styles from './App.css';
|
||||||
import DependencyList from './components/DependencyList/DependencyList';
|
import Box from './components/UI/Box/Box';
|
||||||
import Button from './components/UI/Button/Button';
|
import Button from './components/UI/Button/Button';
|
||||||
import Modal from './components/UI/Modal/Modal';
|
import Configuration from './containers/Configuration/Configuration';
|
||||||
|
import CSSModules from 'react-css-modules';
|
||||||
|
import DependencyList from './components/DependencyList/DependencyList';
|
||||||
import DownloadProgress from './components/DownloadProgress/DownloadProgress';
|
import DownloadProgress from './components/DownloadProgress/DownloadProgress';
|
||||||
import UpgradeUI from './components/UpgradeUI/UpgradeUI';
|
import DropDown from './components/UI/DropDown/DropDown';
|
||||||
import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
|
|
||||||
import Loading from './components/UI/Loading/Loading';
|
import Loading from './components/UI/Loading/Loading';
|
||||||
|
import Modal from './components/UI/Modal/Modal';
|
||||||
|
import MountItems from './containers/MountItems/MountItems';
|
||||||
|
import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
|
||||||
|
import UpgradeUI from './components/UpgradeUI/UpgradeUI';
|
||||||
|
|
||||||
const Scheduler = require('node-schedule');
|
const Scheduler = require('node-schedule');
|
||||||
|
|
||||||
let ipcRenderer = null;
|
let ipcRenderer = null;
|
||||||
@@ -208,6 +210,7 @@ class App extends Component {
|
|||||||
AllowOptions: false,
|
AllowOptions: false,
|
||||||
AllowDownload: false,
|
AllowDownload: false,
|
||||||
AutoMountChecked: false,
|
AutoMountChecked: false,
|
||||||
|
ConfigStorageType: null,
|
||||||
DownloadActive: false,
|
DownloadActive: false,
|
||||||
DownloadProgress: 0.0,
|
DownloadProgress: 0.0,
|
||||||
DownloadingDependency: false,
|
DownloadingDependency: false,
|
||||||
@@ -313,6 +316,18 @@ class App extends Component {
|
|||||||
this.saveState(this.state.Release, this.state.Version, sia, hyperspace);
|
this.saveState(this.state.Release, this.state.Version, sia, hyperspace);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleConfigClicked = (storageType) => {
|
||||||
|
this.setState({
|
||||||
|
ConfigStorageType: storageType,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
handleConfigClosed = () => {
|
||||||
|
this.setState({
|
||||||
|
ConfigStorageType: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
handleDependencyDownload = (url) => {
|
handleDependencyDownload = (url) => {
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
const items = url.split('/');
|
const items = url.split('/');
|
||||||
@@ -440,30 +455,62 @@ class App extends Component {
|
|||||||
const selectedVersion = (this.state.Version === -1) ?
|
const selectedVersion = (this.state.Version === -1) ?
|
||||||
'unavailable' :
|
'unavailable' :
|
||||||
this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
||||||
|
|
||||||
const downloadEnabled = this.state.AllowDownload &&
|
const downloadEnabled = this.state.AllowDownload &&
|
||||||
!this.state.MountsBusy &&
|
!this.state.MountsBusy &&
|
||||||
!this.state.DownloadActive &&
|
!this.state.DownloadActive &&
|
||||||
(((selectedVersion !== 'unavailable') && (selectedVersion !== this.state.RepertoryVersion)));
|
(selectedVersion !== 'unavailable') &&
|
||||||
|
(selectedVersion !== this.state.RepertoryVersion);
|
||||||
|
|
||||||
const allowMount = this.state.RepertoryVersion !== 'none';
|
const allowMount = this.state.RepertoryVersion !== 'none';
|
||||||
const missingDependencies = (this.state.MissingDependencies.length > 0);
|
const missingDependencies = (this.state.MissingDependencies.length > 0);
|
||||||
|
const allowConfig = this.state.LocationsLookup[selectedVersion] &&
|
||||||
|
!this.state.LocationsLookup[selectedVersion].config_allowed;
|
||||||
|
|
||||||
|
const showDependencies = missingDependencies &&
|
||||||
|
!this.state.DownloadActive;
|
||||||
|
|
||||||
|
const showConfig = !missingDependencies &&
|
||||||
|
this.state.ConfigStorageType &&
|
||||||
|
allowConfig;
|
||||||
|
|
||||||
|
const showUpgrade = !showConfig &&
|
||||||
|
!missingDependencies &&
|
||||||
|
!this.state.DownloadActive &&
|
||||||
|
this.state.UpgradeAvailable &&
|
||||||
|
!this.state.UpgradeDismissed;
|
||||||
|
|
||||||
|
let configDisplay = null;
|
||||||
|
if (showConfig) {
|
||||||
|
configDisplay = (
|
||||||
|
<Modal>
|
||||||
|
<Configuration closed={this.handleConfigClosed}
|
||||||
|
directory={Constants.DATA_LOCATIONS[this.state.Platform]}
|
||||||
|
storageType={this.state.ConfigStorageType}
|
||||||
|
version={selectedVersion} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mountDisplay = null;
|
let mountDisplay = null;
|
||||||
if (allowMount) {
|
if (allowMount) {
|
||||||
mountDisplay = <MountItems platform={this.state.Platform}
|
mountDisplay = <MountItems allowConfig={allowConfig}
|
||||||
sia={this.state.Sia}
|
|
||||||
hyperspace={this.state.Hyperspace}
|
|
||||||
changed={this.handleMountLocationChanged}
|
|
||||||
processAutoMount={!this.state.AutoMountChecked}
|
|
||||||
autoMountProcessed={this.notifyAutoMountProcessed}
|
|
||||||
autoMountChanged={this.handleAutoMountChanged}
|
autoMountChanged={this.handleAutoMountChanged}
|
||||||
mountsBusy={this.notifyMountsBusy}
|
autoMountProcessed={this.notifyAutoMountProcessed}
|
||||||
version={this.state.RepertoryVersion}
|
changed={this.handleMountLocationChanged}
|
||||||
|
configClicked={this.handleConfigClicked}
|
||||||
directory={Constants.DATA_LOCATIONS[this.state.Platform]}
|
directory={Constants.DATA_LOCATIONS[this.state.Platform]}
|
||||||
disabled={!allowMount}/>;
|
disabled={!allowMount}
|
||||||
|
hyperspace={this.state.Hyperspace}
|
||||||
|
mountsBusy={this.notifyMountsBusy}
|
||||||
|
platform={this.state.Platform}
|
||||||
|
processAutoMount={!this.state.AutoMountChecked}
|
||||||
|
sia={this.state.Sia}
|
||||||
|
version={this.state.RepertoryVersion}/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dependencyDisplay = null;
|
let dependencyDisplay = null;
|
||||||
if (missingDependencies && !this.state.DownloadActive) {
|
if (showDependencies) {
|
||||||
dependencyDisplay = (
|
dependencyDisplay = (
|
||||||
<Modal>
|
<Modal>
|
||||||
<DependencyList allowDownload={!this.state.DownloadingDependency}
|
<DependencyList allowDownload={!this.state.DownloadingDependency}
|
||||||
@@ -478,8 +525,8 @@ class App extends Component {
|
|||||||
if (this.state.DownloadActive) {
|
if (this.state.DownloadActive) {
|
||||||
downloadDisplay = (
|
downloadDisplay = (
|
||||||
<Modal>
|
<Modal>
|
||||||
<DownloadProgress progress={this.state.DownloadProgress}
|
<DownloadProgress display={this.state.DownloadName}
|
||||||
display={this.state.DownloadName}/>
|
progress={this.state.DownloadProgress}/>
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,19 +534,16 @@ class App extends Component {
|
|||||||
if (this.state.ExtractActive) {
|
if (this.state.ExtractActive) {
|
||||||
releaseDisplay = <h3 style={{textAlign: 'center'}}>{'Activating <' + selectedVersion + '>'}</h3>
|
releaseDisplay = <h3 style={{textAlign: 'center'}}>{'Activating <' + selectedVersion + '>'}</h3>
|
||||||
} else {
|
} else {
|
||||||
releaseDisplay = <Button disabled={!downloadEnabled}
|
releaseDisplay = <Button clicked={this.handleReleaseDownload}
|
||||||
clicked={this.handleReleaseDownload}>Install</Button>;
|
disabled={!downloadEnabled}>Install</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let upgradeDisplay = null;
|
let upgradeDisplay = null;
|
||||||
if (!missingDependencies &&
|
if (showUpgrade) {
|
||||||
!this.state.DownloadActive &&
|
|
||||||
this.state.UpgradeAvailable &&
|
|
||||||
!this.state.UpgradeDismissed) {
|
|
||||||
upgradeDisplay = (
|
upgradeDisplay = (
|
||||||
<Modal>
|
<Modal>
|
||||||
<UpgradeUI upgrade={this.handleUIDownload}
|
<UpgradeUI cancel={()=>this.setState({UpgradeDismissed: true})}
|
||||||
cancel={()=>this.setState({UpgradeDismissed: true})}/>
|
upgrade={this.handleUIDownload}/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -510,28 +554,30 @@ class App extends Component {
|
|||||||
<table width='100%' cellPadding='2'>
|
<table width='100%' cellPadding='2'>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width='33.33%'>
|
<td width='33.333%'>
|
||||||
<h2>Release</h2>
|
<h2>Release</h2>
|
||||||
</td>
|
</td>
|
||||||
<td width='33.33%'>
|
<td width='33.333%'>
|
||||||
<h2>Version<UpgradeIcon release available={this.state.VersionAvailable}/></h2>
|
<h2>Version<UpgradeIcon
|
||||||
|
release
|
||||||
|
available={this.state.VersionAvailable}/></h2>
|
||||||
</td>
|
</td>
|
||||||
<td width='33.33%'>
|
<td width='33.333%'>
|
||||||
<h2>Installed</h2>
|
<h2>Installed</h2>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<DropDown disabled={this.state.DownloadActive || this.state.ExtractActive || this.state.MountsBusy}
|
<DropDown changed={this.handleReleaseChanged}
|
||||||
|
disabled={this.state.DownloadActive || this.state.ExtractActive || this.state.MountsBusy}
|
||||||
items={this.state.ReleaseTypes}
|
items={this.state.ReleaseTypes}
|
||||||
selected={this.state.Release}
|
selected={this.state.Release}/>
|
||||||
changed={this.handleReleaseChanged}/>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<DropDown disabled={this.state.DownloadActive || this.state.ExtractActive || this.state.MountsBusy}
|
<DropDown changed={this.handleVersionChanged}
|
||||||
|
disabled={this.state.DownloadActive || this.state.ExtractActive || this.state.MountsBusy}
|
||||||
items={this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]]}
|
items={this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]]}
|
||||||
selected={this.state.Version}
|
selected={this.state.Version}/>
|
||||||
changed={this.handleVersionChanged}/>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{this.state.RepertoryVersion}
|
{this.state.RepertoryVersion}
|
||||||
@@ -558,15 +604,24 @@ class App extends Component {
|
|||||||
{dependencyDisplay}
|
{dependencyDisplay}
|
||||||
{upgradeDisplay}
|
{upgradeDisplay}
|
||||||
{downloadDisplay}
|
{downloadDisplay}
|
||||||
<Box dxDark dxStyle={{height: '9.3%', padding: '2px'}}>
|
{configDisplay}
|
||||||
<table cellPadding={0} cellSpacing={0} style={{margin: 0, padding: 0}}>
|
<Box dxStyle={{height: '9.3%', padding: '2px'}}>
|
||||||
|
<table cellPadding={0}
|
||||||
|
cellSpacing={0}
|
||||||
|
style={{margin: 0, padding: 0}}
|
||||||
|
width="100%">
|
||||||
<tbody style={{margin: 0, padding: 0}}>
|
<tbody style={{margin: 0, padding: 0}}>
|
||||||
<tr style={{margin: 0, padding: 0}}>
|
<tr style={{margin: 0, padding: 0}}>
|
||||||
<td width='33%' style={{margin: 0, padding: 0}}/>
|
<td style={{margin: 0, padding: 0}}
|
||||||
<td width='33%' style={{margin: 0, padding: 0}}>
|
width='33.333%'/>
|
||||||
|
<td style={{margin: 0, padding: 0}}
|
||||||
|
width='33.333%'>
|
||||||
<h1 style={{'textAlign': 'center'}}>{'Repertory UI v' + this.props.version}</h1>
|
<h1 style={{'textAlign': 'center'}}>{'Repertory UI v' + this.props.version}</h1>
|
||||||
</td>
|
</td>
|
||||||
<td width='33%' style={{margin: 0, padding: 0}} align='right' valign='middle'>
|
<td align='right'
|
||||||
|
style={{margin: 0, padding: 0}}
|
||||||
|
valign='middle'
|
||||||
|
width='33.333%'>
|
||||||
<UpgradeIcon
|
<UpgradeIcon
|
||||||
available={this.state.UpgradeAvailable}
|
available={this.state.UpgradeAvailable}
|
||||||
clicked={()=>this.setState({UpgradeDismissed: false})}/>
|
clicked={()=>this.setState({UpgradeDismissed: false})}/>
|
||||||
|
|||||||
BIN
src/assets/images/configure.png
Normal file
BIN
src/assets/images/configure.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
36
src/components/ConfigurationItem/ConfigurationItem.css
Normal file
36
src/components/ConfigurationItem/ConfigurationItem.css
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.ConfigurationItem {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Input {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: var(--border_radius);
|
||||||
|
background: rgba(160, 160, 160, 0.1);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
color: var(--text_color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Select {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: var(--border_radius);
|
||||||
|
background: rgba(160, 160, 160, 0.1);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
color: var(--text_color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Option {
|
||||||
|
background: rgba(10, 10, 15, 0.8);
|
||||||
|
border-color: rgba(10, 10, 20, 0.9);
|
||||||
|
color: var(--text_color);
|
||||||
|
}
|
||||||
107
src/components/ConfigurationItem/ConfigurationItem.js
Normal file
107
src/components/ConfigurationItem/ConfigurationItem.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import CSSModules from 'react-css-modules';
|
||||||
|
import styles from './ConfigurationItem.css';
|
||||||
|
|
||||||
|
export default CSSModules((props) => {
|
||||||
|
const handleChanged = (e) => {
|
||||||
|
const target = e.target;
|
||||||
|
if (target.type === 'checkbox') {
|
||||||
|
target.value = e.target.checked ? "true" : "false";
|
||||||
|
}
|
||||||
|
props.changed(target);
|
||||||
|
};
|
||||||
|
|
||||||
|
let data;
|
||||||
|
switch (props.template.type) {
|
||||||
|
case "bool":
|
||||||
|
data = <input checked={JSON.parse(props.value)}
|
||||||
|
onChange={e=>handleChanged(e)}
|
||||||
|
type={'checkbox'}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "double":
|
||||||
|
data = <input min={0.0}
|
||||||
|
onChange={e=>handleChanged(e)}
|
||||||
|
step={"0.01"}
|
||||||
|
styleName='Input'
|
||||||
|
type={'number'}
|
||||||
|
value={parseFloat(props.value).toFixed(2)}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "list":
|
||||||
|
const options = props.items.map((s, i) => {
|
||||||
|
return (
|
||||||
|
<option styleName='Option' key={i} value={s}>{s}</option>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
data = (
|
||||||
|
<select onChange={e=>handleChanged(e)}
|
||||||
|
styleName='Select'
|
||||||
|
value={props.value}>
|
||||||
|
{options}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "string":
|
||||||
|
data = <input onChange={e=>handleChanged(e)}
|
||||||
|
styleName='Input'
|
||||||
|
type={'text'}
|
||||||
|
value={props.value}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "uint8":
|
||||||
|
data = <input max={255}
|
||||||
|
min={0}
|
||||||
|
onChange={e=>handleChanged(e)}
|
||||||
|
styleName='Input'
|
||||||
|
type={'number'}
|
||||||
|
value={props.value}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "uint16":
|
||||||
|
data = <input max={65535}
|
||||||
|
min={0}
|
||||||
|
onChange={e=>handleChanged(e)}
|
||||||
|
styleName='Input'
|
||||||
|
type={'number'}
|
||||||
|
value={props.value}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "uint32":
|
||||||
|
data = <input max={4294967295}
|
||||||
|
min={0}
|
||||||
|
onChange={e=>handleChanged(e)}
|
||||||
|
styleName='Input'
|
||||||
|
type={'number'}
|
||||||
|
value={props.value}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "uint64":
|
||||||
|
data = <input max={18446744073709551615}
|
||||||
|
min={0}
|
||||||
|
onChange={e=>handleChanged(e)}
|
||||||
|
styleName='Input'
|
||||||
|
type={'number'}
|
||||||
|
value={props.value}/>;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
data = <div>{props.value}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div styleName='ConfigurationItem'>
|
||||||
|
<table cellPadding='2'
|
||||||
|
width='100%'>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width='100%'>{props.label}</td>
|
||||||
|
<td>{data}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, styles, {allowMultiple: true});
|
||||||
@@ -8,15 +8,15 @@ export default CSSModules((props) => {
|
|||||||
const items = props.dependencies.map((k, i)=> {
|
const items = props.dependencies.map((k, i)=> {
|
||||||
return (
|
return (
|
||||||
<Dependency allowDownload={props.allowDownload}
|
<Dependency allowDownload={props.allowDownload}
|
||||||
|
download={k.download}
|
||||||
key={i}
|
key={i}
|
||||||
name={k.display}
|
name={k.display}
|
||||||
download={k.download}
|
|
||||||
onDownload={props.onDownload}/>
|
onDownload={props.onDownload}/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box dxDark dxStyle={{width: '300px', height: 'auto', padding: '5px'}}>
|
<Box dxStyle={{width: '300px', height: 'auto', padding: '5px'}}>
|
||||||
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}>
|
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}>
|
||||||
<h1 style={{width: '100%', textAlign: 'center'}}>Missing Dependencies</h1>
|
<h1 style={{width: '100%', textAlign: 'center'}}>Missing Dependencies</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import styles from './DownloadProgress.css';
|
|||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box dxDark dxStyle={{width: '380px', height: 'auto', padding: '5px'}}>
|
<Box dxStyle={{width: '380px', height: 'auto', padding: '5px'}}>
|
||||||
<div style={{width: '100%', height: 'auto'}}>
|
<div style={{width: '100%', height: 'auto'}}>
|
||||||
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.display}</h1>
|
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.display}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,21 +4,34 @@ import styles from './MountItem.css';
|
|||||||
import DropDown from '../UI/DropDown/DropDown';
|
import DropDown from '../UI/DropDown/DropDown';
|
||||||
import Button from '../UI/Button/Button';
|
import Button from '../UI/Button/Button';
|
||||||
import Loader from 'react-loader-spinner';
|
import Loader from 'react-loader-spinner';
|
||||||
|
import configureImage from '../../assets/images/configure.png';
|
||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
|
let configButton = null;
|
||||||
|
if (props.allowConfig) {
|
||||||
|
configButton = (
|
||||||
|
<img alt=''
|
||||||
|
height={'16px'}
|
||||||
|
onClick={props.configClicked}
|
||||||
|
src={configureImage}
|
||||||
|
style={{padding: 0, border: 0, margin: 0, cursor: 'pointer'}}
|
||||||
|
width={'16px'}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let inputControl = null;
|
let inputControl = null;
|
||||||
let mountWidth = '70%';
|
let mountWidth = '70%';
|
||||||
if (props.platform === 'win32') {
|
if (props.platform === 'win32') {
|
||||||
inputControl = <DropDown disabled={!props.allowMount || props.mounted}
|
inputControl = <DropDown changed={props.changed}
|
||||||
|
disabled={!props.allowMount || props.mounted}
|
||||||
items={props.items}
|
items={props.items}
|
||||||
selected={props.items.indexOf(props.location)}
|
selected={props.items.indexOf(props.location)}/>;
|
||||||
changed={props.changed}/>;
|
|
||||||
mountWidth = '18%';
|
mountWidth = '18%';
|
||||||
} else {
|
} else {
|
||||||
inputControl = <input disabled={!props.allowMount || props.mounted}
|
inputControl = <input disabled={!props.allowMount || props.mounted}
|
||||||
|
onChange={props.changed}
|
||||||
type={'text'}
|
type={'text'}
|
||||||
value={props.location}
|
value={props.location}/>;
|
||||||
onChange={props.changed}/>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let actionDisplay = null;
|
let actionDisplay = null;
|
||||||
@@ -28,21 +41,29 @@ export default CSSModules((props) => {
|
|||||||
} else {
|
} else {
|
||||||
actionDisplay = <Loader color={'var(--heading_text_color)'}
|
actionDisplay = <Loader color={'var(--heading_text_color)'}
|
||||||
height='24px'
|
height='24px'
|
||||||
width='24px'
|
type='Circles'
|
||||||
type='Circles'/>;
|
width='24px'/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div styleName='MountItem'>
|
<div styleName='MountItem'>
|
||||||
<h2>{props.title}</h2>
|
<table><tbody><tr><td>{configButton}</td><td><h2>{props.title}</h2></td></tr></tbody></table>
|
||||||
<table width='100%' cellPadding='2'>
|
<table cellPadding='2'
|
||||||
|
width='100%'>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width={mountWidth} height='30px'>{inputControl}</td>
|
<td height='30px'
|
||||||
<td width='25%' align='center' valign='middle'>
|
width={mountWidth}>{inputControl}
|
||||||
|
</td>
|
||||||
|
<td align='center'
|
||||||
|
valign='middle'
|
||||||
|
width='25%'>
|
||||||
{actionDisplay}
|
{actionDisplay}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type='checkbox' checked={props.autoMount} onChange={props.autoMountChanged}/>Auto-mount
|
<input checked={props.autoMount}
|
||||||
|
onChange={props.autoMountChanged}
|
||||||
|
type='checkbox'/>Auto-mount
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CSSModules from 'react-css-modules';
|
|
||||||
import styles from './DropDown.css';
|
import styles from './DropDown.css';
|
||||||
|
import CSSModules from 'react-css-modules';
|
||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
const options = props.items.map((s, i) => {
|
const options = props.items.map((s, i) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.UpgradeIcon {
|
.UpgradeIcon {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin-right: 2px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export default CSSModules((props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return props.available ?
|
return props.available ?
|
||||||
<img alt='' style={style} styleName='UpgradeIcon' src={availableImage} onClick={props.clicked}/> :
|
<img alt=''
|
||||||
|
onClick={props.clicked}
|
||||||
|
src={availableImage}
|
||||||
|
style={style}
|
||||||
|
styleName='UpgradeIcon'/> :
|
||||||
null;
|
null;
|
||||||
}, styles, {allowMultiple: true});
|
}, styles, {allowMultiple: true});
|
||||||
@@ -6,22 +6,22 @@ import styles from './UpgradeUI.css';
|
|||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
return (
|
return (
|
||||||
<Box dxDark dxStyle={{width: '180px', height: 'auto', padding: '5px'}}>
|
<Box dxStyle={{width: '180px', height: 'auto', padding: '5px'}}>
|
||||||
<div style={{width: '100%', height: 'auto'}}>
|
<div style={{width: '100%', height: 'auto'}}>
|
||||||
<h1 style={{width: '100%', textAlign: 'center'}}>UI Upgrade Available</h1>
|
<h1 style={{width: '100%', textAlign: 'center'}}>UI Upgrade Available</h1>
|
||||||
</div>
|
</div>
|
||||||
<table cellSpacing={5} width="100%">
|
<table cellSpacing={5} width="100%">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="50%">
|
<td width="50%">
|
||||||
<Button buttonStyles={{width: '100%'}}
|
<Button buttonStyles={{width: '100%'}}
|
||||||
clicked={props.upgrade}>Install</Button>
|
clicked={props.upgrade}>Install</Button>
|
||||||
</td>
|
</td>
|
||||||
<td width="50%">
|
<td width="50%">
|
||||||
<Button buttonStyles={{width: '100%'}}
|
<Button buttonStyles={{width: '100%'}}
|
||||||
clicked={props.cancel}>Cancel</Button>
|
clicked={props.cancel}>Cancel</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</Box>);
|
</Box>);
|
||||||
|
|||||||
6
src/containers/Configuration/Configuration.css
Normal file
6
src/containers/Configuration/Configuration.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.Configuration {
|
||||||
|
width: 90vw;
|
||||||
|
height: 90vh;
|
||||||
|
padding: 4px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
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});
|
||||||
@@ -13,66 +13,9 @@ class MountItems extends Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
ipcRenderer.on('detect_mounts_reply', (event, arg) => {
|
ipcRenderer.on('detect_mounts_reply', this.onDetectMountsReply);
|
||||||
if (arg.data.Success) {
|
ipcRenderer.on('mount_drive_reply', this.onMountDriveReply);
|
||||||
const sia = {
|
ipcRenderer.on('unmount_drive_reply', this.onUnmountDriveReply);
|
||||||
...this.state.Sia,
|
|
||||||
AllowMount: true,
|
|
||||||
DriveLetters: (arg.data.DriveLetters.Sia),
|
|
||||||
Mounted: (arg.data.Locations.Sia.length > 0),
|
|
||||||
PID: arg.data.PIDS.Sia,
|
|
||||||
};
|
|
||||||
const hs = {
|
|
||||||
...this.state.Hyperspace,
|
|
||||||
AllowMount: true,
|
|
||||||
DriveLetters: (arg.data.DriveLetters.Hyperspace),
|
|
||||||
Mounted: (arg.data.Locations.Hyperspace.length > 0),
|
|
||||||
PID: arg.data.PIDS.Hyperspace,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
Hyperspace: hs,
|
|
||||||
Sia: sia,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.mountsBusy(hs.Mounted || sia.Mounted);
|
|
||||||
|
|
||||||
let hsLocation = arg.data.Locations.Hyperspace;
|
|
||||||
if ((hsLocation.length === 0) && (this.props.platform === 'win32')) {
|
|
||||||
hsLocation = this.props.hyperspace.MountLocation || arg.data.DriveLetters.Hyperspace[0];
|
|
||||||
}
|
|
||||||
if (hsLocation !== this.props.hyperspace.MountLocation) {
|
|
||||||
this.props.changed('Hyperspace', hsLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
let siaLocation = arg.data.Locations.Sia;
|
|
||||||
if ((siaLocation.length === 0) && (this.props.platform === 'win32')) {
|
|
||||||
siaLocation = this.props.sia.MountLocation || arg.data.DriveLetters.Sia[0];
|
|
||||||
}
|
|
||||||
if (siaLocation !== this.props.sia.MountLocation) {
|
|
||||||
this.props.changed('Sia', siaLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.performAutoMount();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('mount_drive_reply', (event, arg) => {
|
|
||||||
const state = {
|
|
||||||
...this.state[arg.data.StorageType],
|
|
||||||
PID: arg.data.PID,
|
|
||||||
Mounted: arg.data.Success,
|
|
||||||
};
|
|
||||||
this.setState({
|
|
||||||
[arg.data.StorageType]: state,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.detectMounts();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('unmount_drive_reply', (event, arg) => {
|
|
||||||
this.detectMounts();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.detectMounts();
|
this.detectMounts();
|
||||||
}
|
}
|
||||||
@@ -93,6 +36,14 @@ class MountItems extends Component {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
if (ipcRenderer) {
|
||||||
|
ipcRenderer.removeListener('detect_mounts_reply', this.onDetectMountsReply);
|
||||||
|
ipcRenderer.removeListener('mount_drive_reply', this.onMountDriveReply);
|
||||||
|
ipcRenderer.removeListener('unmount_drive_reply', this.onUnmountDriveReply);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
detectMounts = ()=> {
|
detectMounts = ()=> {
|
||||||
this.props.mountsBusy(true);
|
this.props.mountsBusy(true);
|
||||||
ipcRenderer.send('detect_mounts', {
|
ipcRenderer.send('detect_mounts', {
|
||||||
@@ -141,6 +92,67 @@ class MountItems extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onDetectMountsReply = (event, arg) => {
|
||||||
|
if (arg.data.Success) {
|
||||||
|
const sia = {
|
||||||
|
...this.state.Sia,
|
||||||
|
AllowMount: true,
|
||||||
|
DriveLetters: (arg.data.DriveLetters.Sia),
|
||||||
|
Mounted: (arg.data.Locations.Sia.length > 0),
|
||||||
|
PID: arg.data.PIDS.Sia,
|
||||||
|
};
|
||||||
|
const hs = {
|
||||||
|
...this.state.Hyperspace,
|
||||||
|
AllowMount: true,
|
||||||
|
DriveLetters: (arg.data.DriveLetters.Hyperspace),
|
||||||
|
Mounted: (arg.data.Locations.Hyperspace.length > 0),
|
||||||
|
PID: arg.data.PIDS.Hyperspace,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
Hyperspace: hs,
|
||||||
|
Sia: sia,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.mountsBusy(hs.Mounted || sia.Mounted);
|
||||||
|
|
||||||
|
let hsLocation = arg.data.Locations.Hyperspace;
|
||||||
|
if ((hsLocation.length === 0) && (this.props.platform === 'win32')) {
|
||||||
|
hsLocation = this.props.hyperspace.MountLocation || arg.data.DriveLetters.Hyperspace[0];
|
||||||
|
}
|
||||||
|
if (hsLocation !== this.props.hyperspace.MountLocation) {
|
||||||
|
this.props.changed('Hyperspace', hsLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
let siaLocation = arg.data.Locations.Sia;
|
||||||
|
if ((siaLocation.length === 0) && (this.props.platform === 'win32')) {
|
||||||
|
siaLocation = this.props.sia.MountLocation || arg.data.DriveLetters.Sia[0];
|
||||||
|
}
|
||||||
|
if (siaLocation !== this.props.sia.MountLocation) {
|
||||||
|
this.props.changed('Sia', siaLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.performAutoMount();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMountDriveReply = (event, arg) => {
|
||||||
|
const state = {
|
||||||
|
...this.state[arg.data.StorageType],
|
||||||
|
PID: arg.data.PID,
|
||||||
|
Mounted: arg.data.Success,
|
||||||
|
};
|
||||||
|
this.setState({
|
||||||
|
[arg.data.StorageType]: state,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.detectMounts();
|
||||||
|
};
|
||||||
|
|
||||||
|
onUnmountDriveReply = (event, arg) => {
|
||||||
|
this.detectMounts();
|
||||||
|
};
|
||||||
|
|
||||||
performAutoMount = ()=> {
|
performAutoMount = ()=> {
|
||||||
if (this.props.processAutoMount) {
|
if (this.props.processAutoMount) {
|
||||||
this.props.autoMountProcessed();
|
this.props.autoMountProcessed();
|
||||||
@@ -160,28 +172,32 @@ class MountItems extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div styleName='MountItems'>
|
<div styleName='MountItems'>
|
||||||
<MountItem allowMount={this.state.Hyperspace.AllowMount}
|
<MountItem allowConfig={this.props.allowConfig}
|
||||||
|
allowMount={this.state.Hyperspace.AllowMount}
|
||||||
autoMount={this.props.hyperspace.AutoMount}
|
autoMount={this.props.hyperspace.AutoMount}
|
||||||
autoMountChanged={(e)=>this.props.autoMountChanged('Hyperspace', e)}
|
autoMountChanged={(e)=>this.props.autoMountChanged('Hyperspace', e)}
|
||||||
mounted={this.state.Hyperspace.Mounted}
|
|
||||||
items={this.state.Hyperspace.DriveLetters}
|
|
||||||
platform={this.props.platform}
|
|
||||||
title={'Hyperspace'}
|
|
||||||
location={this.props.hyperspace.MountLocation}
|
|
||||||
changed={(e) => this.handleMountLocationChanged('Hyperspace', e.target.value)}
|
changed={(e) => this.handleMountLocationChanged('Hyperspace', e.target.value)}
|
||||||
clicked={this.handleMountUnMount}
|
clicked={this.handleMountUnMount}
|
||||||
pid={this.state.Hyperspace.PID}/>
|
configClicked={()=>this.props.configClicked('Hyperspace')}
|
||||||
<MountItem allowMount={this.state.Sia.AllowMount}
|
items={this.state.Hyperspace.DriveLetters}
|
||||||
|
location={this.props.hyperspace.MountLocation}
|
||||||
|
mounted={this.state.Hyperspace.Mounted}
|
||||||
|
pid={this.state.Hyperspace.PID}
|
||||||
|
platform={this.props.platform}
|
||||||
|
title={'Hyperspace'}/>
|
||||||
|
<MountItem allowConfig={this.props.allowConfig}
|
||||||
|
allowMount={this.state.Sia.AllowMount}
|
||||||
autoMount={this.props.sia.AutoMount}
|
autoMount={this.props.sia.AutoMount}
|
||||||
autoMountChanged={(e)=>this.props.autoMountChanged('Sia', e)}
|
autoMountChanged={(e)=>this.props.autoMountChanged('Sia', e)}
|
||||||
mounted={this.state.Sia.Mounted}
|
|
||||||
items={this.state.Sia.DriveLetters}
|
|
||||||
platform={this.props.platform}
|
|
||||||
title={'Sia'}
|
|
||||||
location={this.props.sia.MountLocation}
|
|
||||||
changed={(e) => this.handleMountLocationChanged('Sia', e.target.value)}
|
changed={(e) => this.handleMountLocationChanged('Sia', e.target.value)}
|
||||||
clicked={this.handleMountUnMount}
|
clicked={this.handleMountUnMount}
|
||||||
pid={this.state.Sia.PID}/>
|
configClicked={()=>this.props.configClicked('Sia')}
|
||||||
|
items={this.state.Sia.DriveLetters}
|
||||||
|
location={this.props.sia.MountLocation}
|
||||||
|
mounted={this.state.Sia.Mounted}
|
||||||
|
pid={this.state.Sia.PID}
|
||||||
|
platform={this.props.platform}
|
||||||
|
title={'Sia'}/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,18 @@
|
|||||||
--control_border: 1px solid rgba(70, 70, 70, 0.9);
|
--control_border: 1px solid rgba(70, 70, 70, 0.9);
|
||||||
--control_box_shadow: 1px 1px 1px black;
|
--control_box_shadow: 1px 1px 1px black;
|
||||||
--control_transparent_background: rgba(60, 60, 70, 0.4);
|
--control_transparent_background: rgba(60, 60, 70, 0.4);
|
||||||
--control_dark_transparent_background: rgba(60, 60, 70, 0.4);
|
--control_dark_transparent_background: rgba(15, 15, 15, 0.8);
|
||||||
|
|
||||||
--text_color: rgba(200, 205, 220, 0.7);
|
--text_color: rgba(200, 205, 220, 0.75);
|
||||||
--text_color_hover: rgba(200, 205, 220, 0.7);
|
--text_color_hover: rgba(200, 205, 220, 0.75);
|
||||||
--heading_text_color: rgba(194, 217, 255, 0.6);
|
--heading_text_color: rgba(140, 169, 203, 0.75);
|
||||||
--heading_other_text_color: rgba(200, 205, 220, 0.7);
|
--heading_other_text_color: rgba(200, 205, 220, 0.75);
|
||||||
--text_color_transition: color 0.3s;
|
--text_color_transition: color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: 'Nunito', sans-serif;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-moz-focus-inner {
|
*::-moz-focus-inner {
|
||||||
|
|||||||
Reference in New Issue
Block a user