Partial import processing

This commit is contained in:
2020-06-16 11:26:09 -05:00
parent 5e6431d630
commit 38bb2855b6
12 changed files with 172 additions and 87 deletions

View File

@@ -35,6 +35,7 @@ import YesNo from './components/YesNo/YesNo';
import {createModalConditionally} from './utils';
import SkynetImport from './containers/SkynetImport/SkynetImport';
import {displaySkynetImport} from './redux/actions/skynet_actions';
import ApplicationBusy from './components/ApplicationBusy/ApplicationBusy';
const Constants = require('./constants');
const Scheduler = require('node-schedule');
@@ -175,6 +176,8 @@ class App extends IPCContainer {
<SelectAppPlatform/>);
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI/>);
const importDisplay = createModalConditionally(showSkynetImport, <SkynetImport version={selectedVersion}/>);
const appBusyDisplay = createModalConditionally(this.props.AppBusy,
<ApplicationBusy/>, false, true, this.props.AppBusyTransparent);
let mainContent = [];
if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
@@ -206,7 +209,7 @@ class App extends IPCContainer {
));
} else if (selectedVersion !== 'unavailable') {
mainContent.push((
<Box dxStyle={{padding: 'var(--default_spacing)', height: '173px'}}
<Box dxStyle={{padding: 'var(--default_spacing)', height: '170px'}}
key={'md_' + key++}>
<Loading/>
</Box>
@@ -252,6 +255,7 @@ class App extends IPCContainer {
{confirmDisplay}
{downloadDisplay}
{rebootDisplay}
{appBusyDisplay}
{errorDisplay}
</div>
);
@@ -263,6 +267,8 @@ const mapStateToProps = state => {
AllowDownload: state.download.AllowDownload,
AllowMount: state.common.AllowMount,
AppPlatform: state.common.AppPlatform,
AppBusy: state.common.AppBusy,
AppBusyTransparent: state.common.AppBusyTransparent,
AppReady: state.common.AppReady,
DismissDependencies: state.install.DismissDependencies,
DisplayConfiguration: state.mounts.DisplayConfiguration,

View File

@@ -0,0 +1,21 @@
import React from 'react';
import Box from '../UI/Box/Box';
import Loader from 'react-loader-spinner';
import Text from '../UI/Text/Text';
export default ({title}) => {
return (
<Box dxStyle={{padding: 'var(--default_spacing)'}}>
<Text
text={title || 'Please Wait...'}
textAlign={'center'}
type={'Heading1'}/>
<div style={{paddingLeft: 'calc(50% - 16px)', paddingTop: 'var(--default_spacing)'}}>
<Loader color={'var(--heading_text_color)'}
height={32}
width={32}
type='TailSpin'/>
</div>
</Box>
);
}

View File

@@ -2,7 +2,7 @@ import React from 'react';
import './Loading.css'
import Loader from 'react-loader-spinner';
export default props => {
export default () => {
return (
<div
className={'Loading'}>
@@ -13,4 +13,4 @@ export default props => {
type='ThreeDots'/>
</div>
</div>);
};
};

View File

@@ -8,6 +8,10 @@
z-index: 2000;
}
.Modal.Transparent {
background-color: rgba(0, 0, 0, 0);
}
.ModalContent {
position: fixed;
width: auto;

View File

@@ -13,6 +13,10 @@ export default props => {
contentStyles.push('ModalCritical');
}
if (props.transparent) {
modalStyles.push('Transparent');
}
return (
<FocusTrap active={!props.disableFocusTrap}>
<div

View File

@@ -1,38 +1,55 @@
import React from 'react'
import {Component} from 'react';
import {connect} from 'react-redux';
import './SkynetImport.css'
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import {
displaySkynetImport, importSkylinks
} from '../../redux/actions/skynet_actions';
import {displaySkynetImport} from '../../redux/actions/skynet_actions';
//import {ImportList} from './ImportList/ImportList'
import IPCContainer from '../IPCContainer/IPCContainer';
import {notifyApplicationBusy} from '../../redux/actions/common_actions';
import {
notifyError,
notifyInfo
} from '../../redux/actions/error_actions';
const Constants = require('../../constants');
const mapStateToProps = (state, ownProps) => {
return {
AppBusy: state.common.AppBusy,
};
};
const mapDispatchToProps = dispatch => {
return {
displaySkynetImport: display => dispatch(displaySkynetImport(display)),
notifyInfo: msg => dispatch(notifyInfo('Import Syntax', msg)),
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
notifyError: msg => dispatch(notifyError(msg)),
importSkylinks: (version, jsonArray) => dispatch(importSkylinks(version, jsonArray)),
notifyInfo: msg => dispatch(notifyInfo('Import Syntax', msg)),
}
};
export default connect(null, mapDispatchToProps)(class extends Component {
export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer {
state = {
import_list: '',
import_text: '',
imports_array: [],
second_stage: false,
};
componentDidMount() {
this.setRequestHandler(Constants.IPC_Import_Skylinks_Reply, this.onImportSkylinksReply);
}
componentWillUnmount() {
super.componentWillUnmount();
}
displaySyntax = () => {
const msg = '!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' +
' AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA\n' +
' AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg\n' +
'\n' +
'To import Skylinks to new or existing directories, use the following syntax:\n' +
'To import Skylinks into new or existing directories, use the following syntax:\n' +
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA"\n' +
' directory="/my/sub/dir2",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg"\n' +
'\n' +
@@ -40,7 +57,7 @@ export default connect(null, mapDispatchToProps)(class extends Component {
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",token="My Password"\n' +
' directory="/my/sub/dir",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",token="My Password"\n' +
'\n' +
'To import JSON Skylinks, use the following syntax:\n' +
'To import Skylinks using JSON syntax:\n' +
' [\n' +
' {\n' +
' "directory": "/",\n' +
@@ -56,56 +73,87 @@ export default connect(null, mapDispatchToProps)(class extends Component {
this.props.notifyInfo(msg)
}
onImportSkylinksReply = (_, arg) => {
this.props.notifyApplicationBusy(false);
if (arg.data.Success) {
console.log(arg.data.Result);
this.setState({
import_text: '',
imports_array: [],
second_stage: false,
});
} else {
this.props.notifyError(arg.data.Error);
}
};
processNext = () => {
const items = this.state.import_list.split('\n');
let importsArray = [];
try {
for (let item of items) {
item = item.trim();
if (item.startsWith('[')) {
importsArray = JSON.parse(this.state.import_list.trim());
break;
} else if (item.indexOf('=') >= 0) {
const parts = item.split(',')
let importItem = {
directory: '/',
skylink: '',
token: '',
};
for (let part of parts) {
part = part.trim();
const pair = part.split('=');
if (pair.length !== 2) {
if (this.state.second_stage) {
try {
this.props.notifyApplicationBusy(true);
this.sendRequest(Constants.IPC_Import_Skylinks, {
Version: this.props.version,
JsonArray: this.state.imports_array,
});
} catch (e) {
this.props.notifyApplicationBusy(false);
this.props.notifyError(e);
}
} else {
const items = this.state.import_text.split('\n');
let importsArray = [];
try {
for (let item of items) {
item = item.trim();
if (item.startsWith('[')) {
importsArray = JSON.parse(this.state.import_text.trim());
break;
} else if (item.indexOf('=') >= 0) {
const parts = item.split(',')
let importItem = {
directory: '/',
skylink: '',
token: '',
};
for (let part of parts) {
part = part.trim();
const pair = part.split('=');
if (pair.length !== 2) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
}
importItem = {
...importItem,
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''),
};
}
if (!importItem.skylink) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
}
importItem = {
...importItem,
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''),
};
importsArray.push(importItem);
} else if (item.length > 0) {
importsArray.push({
directory: '/',
skylink: item,
});
}
if (!importItem.skylink) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
}
importsArray.push(importItem);
} else if (item.length > 0) {
importsArray.push({
directory: '/',
skylink: item,
});
}
}
if (importsArray.length === 0) {
throw new Error('Nothing to import');
if (importsArray.length === 0) {
throw new Error('Nothing to import');
}
this.setState({
imports_array: importsArray,
second_stage: true,
});
} catch (e) {
this.props.notifyError(e);
}
this.props.importSkylinks(this.props.version, importsArray);
} catch (e) {
this.props.notifyError(e);
}
}
render() {
return (
//<ImportList data={this.state.imports_array}/>
return this.props.AppBusy ? (<div/>) : (
<Box dxDark dxStyle={{
height: 'auto',
padding: 'var(--default_spacing)',
@@ -118,15 +166,19 @@ export default connect(null, mapDispatchToProps)(class extends Component {
style={{cursor: 'pointer'}}>X</a>
</div>
<h1 className={'SkynetImportHeading'}>Import List</h1>
<textarea autoFocus={true}
className={'SkynetImportTextArea'}
onChange={e => this.setState({
...this.state,
import_list: e.target.value,
})}
value={this.state.import_list}
rows={10}/>
{
this.state.second_stage ? (
<div/>
) : (
<textarea autoFocus={true}
className={'SkynetImportTextArea'}
onChange={e => this.setState({
import_text: e.target.value,
})}
value={this.state.import_text}
rows={10}/>
)
}
<div className={'SkynetImportButtons'}>
<Button buttonStyles={{height: 'auto', marginTop: 'var(--default_spacing)', width: 'auto'}}
clicked={this.displaySyntax}>Import Syntax...</Button>
@@ -135,7 +187,7 @@ export default connect(null, mapDispatchToProps)(class extends Component {
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto'
}} clicked={this.processNext}>Next</Button>
}} clicked={this.processNext}>{this.state.second_stage ? 'Import' : 'Next'}</Button>
</div>
</Box>
);

View File

@@ -53,6 +53,17 @@ const handleConfirmYesNo = (show, titleOrConfirmed, resolve) => {
};
};
export const NOTIFY_APPLICATION_BUSY = 'common/notifyApplicationBusy';
export const notifyApplicationBusy = (busy, transparent) => {
return {
type: NOTIFY_APPLICATION_BUSY,
payload: {
busy,
transparent
},
};
};
export const notifyRebootRequired = createAction('common/notifyRebootRequired');
export const rebootSystem = () => {

View File

@@ -1,27 +1,4 @@
import {createAction} from '@reduxjs/toolkit';
import {getIPCRenderer} from '../../utils';
import * as Constants from '../../constants';
import {notifyError} from './error_actions';
export const displaySkynetExport = createAction('skynet/displaySkynetExport');
export const displaySkynetImport = createAction('skynet/displaySkynetImport');
export const importSkylinks = (version, jsonArray) => {
return dispatch => {
const ipcRenderer = getIPCRenderer();
if (ipcRenderer) {
ipcRenderer.once(Constants.IPC_Import_Skylinks_Reply, (_, arg) => {
if (arg.data.Success) {
} else {
dispatch(notifyError(arg.data.Error));
}
});
ipcRenderer.send(Constants.IPC_Import_Skylinks, {
Version: version,
JsonArray: jsonArray,
});
}
};
};

View File

@@ -1,6 +1,7 @@
import {createReducer} from '@reduxjs/toolkit';
import {
DISPLAY_CONFIRM_YES_NO,
NOTIFY_APPLICATION_BUSY,
notifyRebootRequired,
setAllowMount,
setApplicationReady,
@@ -11,6 +12,8 @@ import {
export const createCommonReducer = (platformInfo, version) => {
return createReducer({
AllowMount: false,
AppBusy: false,
AppBusyTransparent: false,
AppPlatform: platformInfo.AppPlatform,
AppReady: false,
DisplayConfirmYesNo: false,
@@ -51,6 +54,13 @@ export const createCommonReducer = (platformInfo, version) => {
AppPlatform: action.payload,
}
},
[NOTIFY_APPLICATION_BUSY]: (state, action) => {
return {
...state,
AppBusy: action.payload.busy,
AppBusyTransparent: action.payload.busy && action.payload.transparent,
};
},
[notifyRebootRequired]: (state, action) => {
return {
...state,

View File

@@ -22,8 +22,8 @@ export const checkNewReleases = selectedVersion => {
return [];
};
export const createModalConditionally = (condition, jsx, critical, disableFocusTrap) => {
const modalProps = {critical: critical, disableFocusTrap: disableFocusTrap};
export const createModalConditionally = (condition, jsx, critical, disableFocusTrap, transparent) => {
const modalProps = {critical: critical, disableFocusTrap: disableFocusTrap, transparent: transparent};
return condition ? (<Modal {...modalProps}>{jsx}</Modal>) : null;
};