From 53a236000ed268e4d31688105a38342b985be6b9 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Jun 2020 16:57:57 -0500 Subject: [PATCH] Partial export processing --- package.json | 3 +- src/App.js | 25 +- src/constants.js | 6 + src/containers/SkynetExport/SkynetExport.css | 4 + src/containers/SkynetExport/SkynetExport.js | 144 ++++++++ src/containers/SkynetImport/SkynetImport.js | 20 +- src/helpers.js | 357 ++++++++++++------- src/index.js | 1 + src/renderer/ipc/SkynetIPC.js | 13 + 9 files changed, 436 insertions(+), 137 deletions(-) create mode 100644 src/containers/SkynetExport/SkynetExport.css create mode 100644 src/containers/SkynetExport/SkynetExport.js diff --git a/package.json b/package.json index 6df01a5..5ec9e1c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.28", "@fortawesome/free-solid-svg-icons": "^5.13.0", - "@fortawesome/react-fontawesome": "^0.1.9", + "@fortawesome/react-fontawesome": "^0.1.11", "@reduxjs/toolkit": "^1.3.4", "auto-launch": "^5.0.5", "axios": "^0.19.2", @@ -19,6 +19,7 @@ "node-schedule": "^1.3.2", "randomstring": "^1.1.5", "react": "^16.13.1", + "react-checkbox-tree": "^1.6.0", "react-dom": "^16.13.1", "react-loader-spinner": "^3.1.5", "react-redux": "^7.2.0", diff --git a/src/App.js b/src/App.js index 59037f4..c098dfe 100644 --- a/src/App.js +++ b/src/App.js @@ -36,6 +36,7 @@ import {createModalConditionally} from './utils'; import SkynetImport from './containers/SkynetImport/SkynetImport'; import {displaySkynetImport} from './redux/actions/skynet_actions'; import ApplicationBusy from './components/ApplicationBusy/ApplicationBusy'; +import SkynetExport from './containers/SkynetExport/SkynetExport'; const Constants = require('./constants'); const Scheduler = require('node-schedule'); @@ -162,12 +163,23 @@ class App extends IPCContainer { !showUpgrade && this.props.DisplayImport; - const configDisplay = createModalConditionally(showConfig, ); + const showSkynetExport = !showConfig && + !showDependencies && + !this.props.DownloadActive && + !showNewReleases && + !this.props.RebootRequired && + !this.props.DisplaySelectAppPlatform && + !showUpgrade && + this.props.DisplayExport; + + const configDisplay = createModalConditionally(showConfig, ); const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, ); const dependencyDisplay = createModalConditionally(showDependencies, , false, this.props.InstallActive); - const downloadDisplay = createModalConditionally(this.props.DownloadActive, , false, true); + const downloadDisplay = createModalConditionally(this.props.DownloadActive, + , false, true); const errorDisplay = createModalConditionally(this.props.DisplayError, , true); const infoDisplay = createModalConditionally(this.props.DisplayInfo, , true); const newReleasesDisplay = createModalConditionally(showNewReleases, ); @@ -175,7 +187,10 @@ class App extends IPCContainer { const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, ); const upgradeDisplay = createModalConditionally(showUpgrade, ); - const importDisplay = createModalConditionally(showSkynetImport, ); + const importDisplay = createModalConditionally(showSkynetImport, ); + const exportDisplay = createModalConditionally(showSkynetExport, ) const appBusyDisplay = createModalConditionally(this.props.AppBusy, , false, true, this.props.AppBusyTransparent); @@ -246,6 +261,7 @@ class App extends IPCContainer { {importDisplay} + {exportDisplay} {newReleasesDisplay} {selectAppPlatformDisplay} {dependencyDisplay} @@ -274,6 +290,7 @@ const mapStateToProps = state => { DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayConfirmYesNo: state.common.DisplayConfirmYesNo, DisplayError: state.error.DisplayError, + DisplayExport: state.skynet.DisplayExport, DisplayImport: state.skynet.DisplayImport, DisplayInfo: state.error.DisplayInfo, DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform, diff --git a/src/constants.js b/src/constants.js index 70a24a8..fa0631f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -99,6 +99,9 @@ exports.IPC_Download_File = 'download_file'; exports.IPC_Download_File_Complete = 'download_file_complete'; exports.IPC_Download_File_Progress = 'download_file_progress'; +exports.IPC_Export_Skylinks = 'export_skylinks'; +exports.IPC_Export_Skylinks_Reply = 'export_skylinks_reply'; + exports.IPC_Extract_Release = 'extract_release'; exports.IPC_Extract_Release_Complete = 'extract_release_complete'; @@ -114,6 +117,9 @@ exports.IPC_Get_Platform_Reply = 'get_platform_reply'; exports.IPC_Get_State = 'get_state'; exports.IPC_Get_State_Reply = 'get_state_reply'; +exports.IPC_Grab_Skynet_Tree = 'grab_skynet_tree'; +exports.IPC_Grab_Skynet_Tree_Reply = 'grab_skynet_tree_reply'; + exports.IPC_Import_Skylinks = 'import_skylinks'; exports.IPC_Import_Skylinks_Reply = 'import_skylinks_reply'; diff --git a/src/containers/SkynetExport/SkynetExport.css b/src/containers/SkynetExport/SkynetExport.css new file mode 100644 index 0000000..b1db5c3 --- /dev/null +++ b/src/containers/SkynetExport/SkynetExport.css @@ -0,0 +1,4 @@ +.SkynetExportHeading { + text-align: center; + padding-bottom: var(--default_spacing); +} diff --git a/src/containers/SkynetExport/SkynetExport.js b/src/containers/SkynetExport/SkynetExport.js new file mode 100644 index 0000000..b792e02 --- /dev/null +++ b/src/containers/SkynetExport/SkynetExport.js @@ -0,0 +1,144 @@ +import React from 'react'; +import './SkynetExport.css'; +import CheckboxTree from 'react-checkbox-tree'; +import {connect} from 'react-redux'; +import IPCContainer from '../IPCContainer/IPCContainer'; +import {notifyApplicationBusy} from '../../redux/actions/common_actions'; +import {notifyError} from '../../redux/actions/error_actions'; +import Box from '../../components/UI/Box/Box'; +import {displaySkynetExport} from '../../redux/actions/skynet_actions'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import { + faCheckSquare, faChevronDown, + faChevronRight, faFile, faFolder, faFolderOpen, + faHSquare, faMinusSquare, faPlusSquare, + faSquare, faSquareFull, faSquareRootAlt +} from '@fortawesome/free-solid-svg-icons'; + +const Constants = require('../../constants'); + +const mapStateToProps = state => { + return { + AppBusy: state.common.AppBusy, + }; +}; + +const mapDispatchToProps = dispatch => { + return { + displaySkynetExport: display => dispatch(displaySkynetExport(display)), + notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)), + notifyError: msg => dispatch(notifyError(msg)), + } +}; + +export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer { + state = { + checked: [], + expanded: [], + nodes: [], + } + + componentDidMount() { + this.setRequestHandler(Constants.IPC_Grab_Skynet_Tree_Reply, this.onGrabSkynetTreeReply); + this.setRequestHandler(Constants.IPC_Export_Skylinks_Reply, this.onExportSkylinksReply); + this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version}); + } + + componentWillUnmount() { + super.componentWillUnmount(); + } + + createNodes = items => { + /* + { + name: '', + path: '', + directory: true/false, + children: [], + } + */ + const ret = []; + for (const item of items) { + const treeItem = { + label: item.name, + path: item.path, + value: item.name === '/' ? 0 : JSON.stringify(item), + }; + + if (item.directory) { + treeItem.children = this.createNodes(item.children); + } + + ret.push(treeItem); + } + + return ret; + } + + onGrabSkynetTreeReply = (_, arg) => { + if (arg.data.Success) { + this.setState({ + checked: [], + expanded: [0], + nodes: this.createNodes(arg.data.Result), + }); + } else { + this.setState({ + checked: [], + expanded: [], + nodes: [], + }, () => { + this.props.notifyError(arg.data.Error); + }); + } + } + + onExportSkylinksReply = (_, arg) => { + + } + + render() { + console.log(this.state.expanded); + return this.props.AppBusy ? (
) : ( + + +

{'Export Files'}

+ , + uncheck: , + halfCheck: , + expandClose: , + expandOpen: , + expandAll: , + collapseAll: , + parentClose: , + parentOpen: , + leaf: + }} + nodes={this.state.nodes} + onCheck={checked => this.setState({checked})} + onExpand={expanded => this.setState({expanded})}/> +
+ ); + } +}); diff --git a/src/containers/SkynetImport/SkynetImport.js b/src/containers/SkynetImport/SkynetImport.js index 4b225fe..86ea707 100644 --- a/src/containers/SkynetImport/SkynetImport.js +++ b/src/containers/SkynetImport/SkynetImport.js @@ -166,12 +166,20 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon width: 'calc(100vw - (var(--default_spacing) * 4)' }}>
+ style={{ + float: 'right', + margin: 0, + padding: 0, + marginTop: '-4px', + boxSizing: 'border-box', + display: 'block' + }}> this.props.displaySkynetImport(false)} style={{cursor: 'pointer'}}>X
-

{this.state.second_stage ? 'Verify Imports' : 'Import List'}

+

{this.state.second_stage ? 'Verify Imports' : 'Import List'}

{ this.state.second_stage ? ( @@ -186,8 +194,9 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon ) }
- +
{ this.state.second_stage ? @@ -206,7 +215,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon marginLeft: 'var(--default_spacing)', marginTop: 'var(--default_spacing)', width: 'auto' - }} clicked={this.handleNavigation}>{this.state.second_stage ? 'Import' : 'Next'} + }} + clicked={this.handleNavigation}>{this.state.second_stage ? 'Import' : 'Next'}
diff --git a/src/helpers.js b/src/helpers.js index 4d0c4f6..817d6d9 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -9,7 +9,6 @@ const Constants = require('./constants'); const RandomString = require('randomstring'); let vcRuntimeExists; - const _vcRuntimeExists = () => { return new Promise((resolve, reject) => { if (os.platform() !== 'win32') { @@ -19,7 +18,7 @@ const _vcRuntimeExists = () => { resolve(true); } else { const cmd = path.join(process.env.windir, 'system32', 'reg.exe'); - const args = ["QUERY", "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"]; + const args = ['QUERY', 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall']; _execProcessGetOutput(cmd, null, args) .then(lines => { const parseLine = index => { @@ -62,7 +61,93 @@ const _vcRuntimeExists = () => { }); }; -const _executeProcess = (command, working, args=[]) => { +// https://stackoverflow.com/questions/19531453/transform-file-directory-structure-into-tree-in-javascript +const _createTreeNodes = fileList => { + let tree = {} + + const directorySort = (a, b) => { + return !!a.directory === !!b.directory ? a.name.localeCompare(b.name) : a.directory ? -1 : 1; + }; + + const addNode = obj => { + const fullPath = path.join(obj.directory, obj.filename).replace(/\\/g, '/'); + const pathParts = fullPath.replace(/^\/|\/$/g, '').split('/'); + let ptr = tree; + for (let i = 0; i < pathParts.length; i++) { + const node = { + directory: true, + name: pathParts[i], + }; + if (i === pathParts.length - 1) { + node.directory = false; + node.path = fullPath; + } + ptr[pathParts[i]] = ptr[pathParts[i]] || node; + ptr[pathParts[i]].children = ptr[pathParts[i]].children || {}; + ptr = ptr[pathParts[i]].children; + } + } + + const objectToArray = node => { + Object.keys(node || {}).map((k) => { + if (node[k].children) { + objectToArray(node[k]) + } + }) + if (node.children) { + node.children = Object.values(node.children); + node.children.forEach(objectToArray) + node.children = node.children.sort(directorySort); + } + } + + fileList.map(addNode); + objectToArray(tree); + return Object.values(tree).sort(directorySort); +}; + +const _exportAllSkylinks = version => { + return new Promise((resolve, reject) => { + const repertoryExec = _getRepertoryExec(version); + const processOptions = { + cwd: repertoryExec.working, + detached: true, + shell: false, + windowsHide: true, + }; + + const args = _getDefaultRepertoryArgs('Skynet'); + args.push('-ea'); + + let result = ''; + const process = new spawn(repertoryExec.cmd, args, processOptions); + + process.on('error', (err) => { + reject(err); + }); + + process.stdout.on('data', (d) => { + result += d; + }); + + process.stderr.on('data', (d) => { + result += d; + }); + + process.on('exit', code => { + if (code === 0) { + result = result.substr(result.indexOf('{')); + resolve(JSON.parse(result)); + } else { + reject(new Error('Failed to import: ' + code + ':' + result)); + } + }); + + process.unref(); + }); +}; + +const _executeProcess = (command, working, args = []) => { return new Promise((resolve, reject) => { let processOptions = { detached: true, @@ -144,16 +229,16 @@ const _getRepertoryExec = version => { const _removeDirectoryRecursively = dir => { if (fs.existsSync(dir)) { fs - .readdirSync(dir) - .forEach(file => { - const curPath = path.join(dir, file); - if (fs.lstatSync(curPath).isDirectory()) { - module.exports.removeDirectoryRecursively(curPath); - } else { - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(dir); + .readdirSync(dir) + .forEach(file => { + const curPath = path.join(dir, file); + if (fs.lstatSync(curPath).isDirectory()) { + module.exports.removeDirectoryRecursively(curPath); + } else { + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(dir); } }; @@ -206,13 +291,13 @@ module.exports.cleanupOldReleases = versionList => { if (versionList && versionList.length > 0) { const dataDir = _getDataDirectory(); const directoryList = fs - .readdirSync(dataDir, {withFileTypes: true}) - .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent); + .readdirSync(dataDir, {withFileTypes: true}) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent); const removeList = directoryList - .filter(dirent => !versionList.includes(dirent.name)) - .map(dirent => dirent.name); + .filter(dirent => !versionList.includes(dirent.name)) + .map(dirent => dirent.name); for (const dir of removeList) { try { @@ -320,51 +405,51 @@ module.exports.downloadFile = (url, destination, progressCallback, completeCallb } axios - .get(url, { - responseType: 'stream', - }) - .then(response => { - try { - const total = parseInt(response.headers['content-length'], 10); - if (total === 0) { - completeCallback(Error('No data available for download')); - } else { - const stream = fs.createWriteStream(destination); + .get(url, { + responseType: 'stream', + }) + .then(response => { + try { + const total = parseInt(response.headers['content-length'], 10); + if (total === 0) { + completeCallback(Error('No data available for download')); + } else { + const stream = fs.createWriteStream(destination); - let downloaded = 0; - response.data.on('data', (chunk) => { - stream.write(Buffer.from(chunk)); - downloaded += chunk.length; - if (progressCallback) { - progressCallback((downloaded / total * 100.0).toFixed(2)); + let downloaded = 0; + response.data.on('data', (chunk) => { + stream.write(Buffer.from(chunk)); + downloaded += chunk.length; + if (progressCallback) { + progressCallback((downloaded / total * 100.0).toFixed(2)); + } + }); + + response.data.on('end', () => { + stream.end(() => { + if (downloaded === 0) { + completeCallback(Error('Received 0 bytes')); + } else if (downloaded !== total) { + completeCallback(Error('Received incorrect number of bytes')); + } else { + completeCallback(); } }); + }); - response.data.on('end', () => { - stream.end(() => { - if (downloaded === 0) { - completeCallback(Error('Received 0 bytes')); - } else if (downloaded !== total) { - completeCallback(Error('Received incorrect number of bytes')); - } else { - completeCallback(); - } - }); + response.data.on('error', error => { + stream.end(() => { + completeCallback(error); }); - - response.data.on('error', error => { - stream.end(() => { - completeCallback(error); - }); - }); - } - } catch (error) { - completeCallback(error); + }); } - }) - .catch(error => { + } catch (error) { completeCallback(error); - }); + } + }) + .catch(error => { + completeCallback(error); + }); }; module.exports.executeAndWait = (command, ignoreResult) => { @@ -392,7 +477,7 @@ module.exports.executeAndWait = (command, ignoreResult) => { }); }; -module.exports.executeAsync = (command, args=[]) => { +module.exports.executeAsync = (command, args = []) => { return new Promise((resolve, reject) => { const launchProcess = (count, timeout) => { let cmd = path.basename(command); @@ -416,7 +501,7 @@ module.exports.executeAsync = (command, args=[]) => { reject(err, pid); } else { clearTimeout(timeout); - setTimeout(()=> launchProcess(count, setTimeout(() => resolve(), 3000)), 1000); + setTimeout(() => launchProcess(count, setTimeout(() => resolve(), 3000)), 1000); } }); @@ -458,7 +543,7 @@ module.exports.executeScript = script => { reject(err); }); - process.stdout.on('data', (d)=> { + process.stdout.on('data', (d) => { result += d; }); @@ -510,6 +595,8 @@ module.exports.executeMount = (version, provider, remote, location, exitCallback }); }; +module.exports.exportAllSkylinks = _exportAllSkylinks; + module.exports.getConfig = (version, provider, remote) => { return new Promise((resolve, reject) => { const repertoryExec = _getRepertoryExec(version); @@ -530,14 +617,14 @@ module.exports.getConfig = (version, provider, remote) => { reject(err); }); - process.stdout.on('data', (d)=> { + process.stdout.on('data', (d) => { result += d; }); process.on('exit', () => { const lines = result - .replace(/\r\n/g, '\n') - .split('\n'); + .replace(/\r\n/g, '\n') + .split('\n'); const code = parseInt(lines[0], 10); if (code === 0) { @@ -574,7 +661,7 @@ module.exports.getConfigTemplate = (version, provider, remote) => { reject(err); }); - process.stdout.on('data', (d)=> { + process.stdout.on('data', (d) => { result += d; }); @@ -607,16 +694,16 @@ module.exports.getMissingDependencies = dependencies => { if (index >= dep.registry.length) { if (dep.display === 'VC Runtime 2015-2019') { _vcRuntimeExists() - .then(exists => { - if (!exists) { - missing.push(dep); - } - resolveIfComplete(); - }) - .catch(() => { + .then(exists => { + if (!exists) { missing.push(dep); - resolveIfComplete(); - }) + } + resolveIfComplete(); + }) + .catch(() => { + missing.push(dep); + resolveIfComplete(); + }) } else { missing.push(dep); resolveIfComplete(); @@ -666,7 +753,7 @@ module.exports.getMissingDependencies = dependencies => { }; for (const dependency of dependencies) { - checkRegistry(dependency,0); + checkRegistry(dependency, 0); } } else { for (const dep of dependencies) { @@ -683,6 +770,22 @@ module.exports.getMissingDependencies = dependencies => { }); }; +module.exports.grabSkynetFileTree = version => { + return new Promise((resolve, reject) => { + _exportAllSkylinks(version) + .then(results => { + resolve([{ + name: '/', + directory: true, + children: _createTreeNodes(results.success), + }]); + }) + .catch(e => { + reject(e); + }); + }); +}; + module.exports.importSkylinks = (version, jsonArray) => { return new Promise((resolve, reject) => { const repertoryExec = _getRepertoryExec(version); @@ -704,11 +807,11 @@ module.exports.importSkylinks = (version, jsonArray) => { reject(err); }); - process.stdout.on('data', (d)=> { + process.stdout.on('data', (d) => { result += d; }); - process.stderr.on('data', (d)=> { + process.stderr.on('data', (d) => { result += d; }); @@ -726,7 +829,7 @@ module.exports.importSkylinks = (version, jsonArray) => { }; //https://stackoverflow.com/questions/31645738/how-to-create-full-path-with-nodes-fs-mkdirsync -module.exports.mkDirByPathSync = (targetDir, { isRelativeToScript = false } = {}) => { +module.exports.mkDirByPathSync = (targetDir, {isRelativeToScript = false} = {}) => { const sep = path.sep; const initDir = path.isAbsolute(targetDir) ? sep : ''; const baseDir = isRelativeToScript ? __dirname : '.'; @@ -761,55 +864,55 @@ module.exports.performWindowsUninstall = names => { reject('Windows OS is not being used'); } else { const cmd = path.join(process.env.windir, 'system32', 'reg.exe'); - const args = ["QUERY", "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"]; + const args = ['QUERY', 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall']; _execProcessGetOutput(cmd, null, args) - .then(lines => { - const parseLine = index => { - if (index < lines.length) { - const line = lines[index]; - if (line.startsWith('HKEY_LOCAL_MACHINE\\')) { - let args2 = JSON.parse(JSON.stringify(args)); - args2[1] = 'HKLM\\' + line.substr(19); - args2.push('/v'); - args2.push('DisplayName'); - args2.push('/t'); - args2.push('REG_SZ'); - _execProcessGetOutput(cmd, null, args2) - .then(lines => { - const value = lines[2].trim().substr(args2[3].length).trim().substr(6).trim(); - if (names.includes(value)) { - const items = line.split('\\'); - const productCode = items[items.length - 1]; - _executeProcess('msiexec.exe', null,['/x', productCode, '/norestart']) - .then(code => { - if ((code === 0) || (code === 3010) || (code === 1641)) { - resolve(true); - } else { - reject('[' + value + '] uninstall failed: ' + code); - } - }) - .catch(err => { - reject(err); - }); + .then(lines => { + const parseLine = index => { + if (index < lines.length) { + const line = lines[index]; + if (line.startsWith('HKEY_LOCAL_MACHINE\\')) { + let args2 = JSON.parse(JSON.stringify(args)); + args2[1] = 'HKLM\\' + line.substr(19); + args2.push('/v'); + args2.push('DisplayName'); + args2.push('/t'); + args2.push('REG_SZ'); + _execProcessGetOutput(cmd, null, args2) + .then(lines => { + const value = lines[2].trim().substr(args2[3].length).trim().substr(6).trim(); + if (names.includes(value)) { + const items = line.split('\\'); + const productCode = items[items.length - 1]; + _executeProcess('msiexec.exe', null, ['/x', productCode, '/norestart']) + .then(code => { + if ((code === 0) || (code === 3010) || (code === 1641)) { + resolve(true); } else { - parseLine(++index); + reject('[' + value + '] uninstall failed: ' + code); } }) - .catch(() => { - parseLine(++index); + .catch(err => { + reject(err); }); - } else { + } else { + parseLine(++index); + } + }) + .catch(() => { parseLine(++index); - } + }); } else { - resolve(false); + parseLine(++index); } - }; - parseLine(0); - }) - .catch( err => { - reject(err); - }); + } else { + resolve(false); + } + }; + parseLine(0); + }) + .catch(err => { + reject(err); + }); } }); }; @@ -898,16 +1001,16 @@ module.exports.testRepertoryBinary = version => { return new Promise((resolve, reject) => { const repertoryExec = _getRepertoryExec(version); _executeProcess(repertoryExec.cmd, repertoryExec.working, ['-dc']) - .then(code => { - if (code === 0) { - resolve(); - } else { - reject(Error('Invalid exit code: ' + code)); - } - }) - .catch(error => { - reject(error); - }); + .then(code => { + if (code === 0) { + resolve(); + } else { + reject(Error('Invalid exit code: ' + code)); + } + }) + .catch(error => { + reject(error); + }); }); }; diff --git a/src/index.js b/src/index.js index 22a184f..2e275e8 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ import {Provider} from 'react-redux'; import {setActiveRelease} from './redux/actions/release_version_actions'; import {setProviderState} from './redux/actions/mount_actions'; import * as serviceWorker from './serviceWorker'; +import 'react-checkbox-tree/lib/react-checkbox-tree.css'; const Constants = require('./constants'); diff --git a/src/renderer/ipc/SkynetIPC.js b/src/renderer/ipc/SkynetIPC.js index fd3d1bf..7701a1c 100644 --- a/src/renderer/ipc/SkynetIPC.js +++ b/src/renderer/ipc/SkynetIPC.js @@ -14,6 +14,19 @@ const addListeners = (ipcMain, {standardIPCReply}) => { standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {}, error); }); }); + + ipcMain.on(Constants.IPC_Grab_Skynet_Tree, (event, data) => { + helpers + .grabSkynetFileTree(data.Version) + .then(result => { + standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, { + Result: result, + }); + }) + .catch(error => { + standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {}, error); + }); + }); }; module.exports = {