Initial Skynet premium portal suppport

This commit is contained in:
2021-05-01 00:41:25 -05:00
parent c5ef4215e1
commit a4c3dbdb94
22 changed files with 631 additions and 66 deletions

View File

@@ -7,8 +7,10 @@
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
"plugin:react/recommended",
"plugin:prettier/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
@@ -17,9 +19,14 @@
"sourceType": "module"
},
"plugins": [
"prettier",
"react",
"jest"
],
"rules": {
"prettier/prettier": "warn",
"react/prop-types": "warn",
"no-prototype-builtins": "warn",
"react/no-string-refs": "warn"
}
}

2
.vimrc
View File

@@ -1,6 +1,6 @@
set autoread
set path+=.,public/**,src/**,test/**
if has('win32')
if has('win32') || has('win64')
let &makeprg="create_dist.cmd"
else
let &makeprg="./create_dist.sh"

View File

@@ -17,6 +17,7 @@
"focus-trap-react": "^8.4.2",
"font-awesome": "^4.7.0",
"node-cron": "1.2.1",
"prop-types": "^15.7.2",
"randomstring": "^1.1.5",
"react": "16.14.0",
"react-checkbox-tree": "^1.6.0",
@@ -37,8 +38,11 @@
"electron-builder": "22.9.1",
"electron-webpack": "^2.8.2",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-jest": "^24.3.2",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.23.0",
"prettier": "2.2.1",
"webpack": "4.44.2",
"webpack-dev-server": "3.11.1"
},

View File

@@ -1,5 +1,6 @@
import React from 'react';
import './App.css';
import AddEditHost from './containers/AddEditHost/AddEditHost';
import Box from './components/UI/Box/Box';
import Configuration from './containers/Configuration/Configuration';
import { connect } from 'react-redux';
@@ -176,6 +177,15 @@ class App extends IPCContainer {
!this.props.DismissNewReleasesAvailable &&
this.props.NewReleasesAvailable.length > 0;
const showAddEditHost =
!showDependencies &&
!this.props.DownloadActive &&
!showNewReleases &&
!this.props.RebootRequired &&
!this.props.DisplaySelectAppPlatform &&
!showUpgrade &&
this.props.DisplayAddEditHost;
const showSkynetImport =
!showConfig &&
!showDependencies &&
@@ -204,6 +214,10 @@ class App extends IPCContainer {
remoteSupported={remoteSupported}
/>
);
const addEditHostDisplay = createModalConditionally(
showAddEditHost,
<AddEditHost />
);
const pinnedManagerDisplay = createModalConditionally(
showPinnedManager,
<PinnedManager version={selectedVersion} />
@@ -358,6 +372,7 @@ class App extends IPCContainer {
{upgradeDisplay}
{pinnedManagerDisplay}
{configDisplay}
{addEditHostDisplay}
{infoDisplay}
{confirmDisplay}
{downloadDisplay}
@@ -378,6 +393,7 @@ const mapStateToProps = (state) => {
AppBusyTransparent: state.common.AppBusyTransparent,
AppReady: state.common.AppReady,
DismissDependencies: state.install.DismissDependencies,
DisplayAddEditHost: state.host.DisplayAddEditHost,
DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayConfirmYesNo: state.common.DisplayConfirmYesNo,
DisplayError: state.error.DisplayError,

View File

@@ -2,6 +2,7 @@ import React from 'react';
import Box from '../UI/Box/Box';
import Loader from 'react-loader-spinner';
import Text from '../UI/Text/Text';
import PropTypes from 'prop-types';
const ApplicationBusy = ({ title }) => {
return (
@@ -27,4 +28,8 @@ const ApplicationBusy = ({ title }) => {
);
};
ApplicationBusy.propTypes = {
title: PropTypes.string,
};
export default ApplicationBusy;

View File

@@ -2,6 +2,7 @@ import React from 'react';
import './DependencyList.css';
import { connect } from 'react-redux';
import * as Constants from '../../constants';
import { createDismissDisplay } from '../../utils.jsx';
import Dependency from './Dependency/Dependency';
import Box from '../UI/Box/Box';
import { downloadItem } from '../../redux/actions/download_actions';
@@ -45,33 +46,12 @@ export default connect(
);
});
const dismissDisplay = (
<div
style={{
float: 'right',
margin: 0,
paddingRight: '4px',
boxSizing: 'border-box',
display: 'block',
}}>
<a
href={'#'}
onClick={
props.AllowDismissDependencies
? () => props.setDismissDependencies(true)
: (e) => e.preventDefault()
}
style={{
cursor: props.AllowDismissDependencies ? 'pointer' : 'no-drop',
}}>
X
</a>
</div>
);
return (
<Box dxStyle={{ width: '300px', height: 'auto', padding: '5px' }}>
{dismissDisplay}
{createDismissDisplay(
() => props.setDismissDependencies(true),
!props.AllowDismissDependencies
)}
<div
style={{
width: '100%',

View File

@@ -0,0 +1,232 @@
import React from 'react';
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import DropDown from '../../components/UI/DropDown/DropDown';
import PropTypes from 'prop-types';
import Text from '../../components/UI/Text/Text';
import { Component } from 'react';
import { connect } from 'react-redux';
import { createDismissDisplay } from '../../utils.jsx';
import {
addEditHost,
completeAddEditHost,
} from '../../redux/actions/host_actions';
class AddEditHost extends Component {
state = {
AgentString: '',
ApiPassword: '',
ApiPort: 443,
AuthPassword: '',
AuthURL: '',
AuthUser: '',
HostNameOrIp: '',
Path: '',
Protocol: 'https',
TimeoutMs: 60000,
};
originalState = {};
componentDidMount() {
if (this.props.HostData) {
const state = { ...this.state, ...this.props.HostData };
this.originalState = { ...state };
this.setState(state);
}
}
handleSave = () => {
this.props.completeAddEditHost(this.state);
};
render() {
return (
<Box dxStyle={{ width: '430px', height: 'auto', padding: '5px' }}>
{createDismissDisplay(this.props.Close)}
<div
style={{
width: '100%',
height: 'auto',
paddingBottom: '5px',
boxSizing: 'border-box',
}}>
<h1
style={{
width: '100%',
textAlign: 'center',
color: 'var(--text_color_error)',
}}>
Portal Settings
</h1>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Host / IP'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) =>
this.setState({ HostNameOrIp: e.target.value.trim() })
}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.HostNameOrIp}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Protocol'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Port'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Timeout (ms)'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<DropDown
changed={(e) => this.setState({ Protocol: e.target.value })}
items={['https', 'http']}
selected={this.state.Protocol}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ ApiPort: e.target.value })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'number'}
min={1}
max={65535}
value={this.state.ApiPort}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ TimeoutMs: e.target.value })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'number'}
min={1000}
step={1000}
value={this.state.TimeoutMs}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text
text={'Agent String (optional)'}
textAlign={'left'}
type={'Heading2'}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<Text
text={'API Key (optional)'}
textAlign={'left'}
type={'Heading2'}
/>
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ AgentString: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.AgentString}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ ApiPassword: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.ApiPassword}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text
text={'Authentication URL (premium)'}
textAlign={'left'}
type={'Heading2'}
/>
</div>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<input
onChange={(e) => this.setState({ AuthURL: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.AuthURL}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text
text={'User Name (premium)'}
textAlign={'left'}
type={'Heading2'}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<Text
text={'Password (premium)'}
textAlign={'left'}
type={'Heading2'}
/>
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ AuthUser: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.AuthUser}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ AuthPassword: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.AuthPassword}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<p>
<b>
{'Portal URL: ' +
this.state.Protocol +
'://' +
this.state.HostNameOrIp +
((this.state.Protocol === 'http' && this.state.ApiPort != 80) ||
(this.state.Protocol === 'https' && this.state.ApiPort != 443)
? ':' + this.state.ApiPort.toString()
: '')}
</b>
</p>
<div style={{ height: 'var(--default_spacing)' }} />
</div>
<Button clicked={this.handleSave}>Save</Button>
</Box>
);
}
}
const mapStateToProps = (state) => {
return {
HostData: state.host.HostData,
};
};
const mapDispatchToProps = (dispatch) => {
return {
Close: () => dispatch(addEditHost(false)),
completeAddEditHost: (host_data) =>
dispatch(completeAddEditHost(true, host_data)),
};
};
AddEditHost.propTypes = {
Close: PropTypes.func.isRequired,
completeAddEditHost: PropTypes.func.isRequired,
HostData: PropTypes.object,
};
export default connect(mapStateToProps, mapDispatchToProps)(AddEditHost);

View File

@@ -36,7 +36,7 @@ const default_state = {
DisplayS3: false,
HostNameOrIp: '',
Name: '',
Port: 20000,
Port: 2000,
Provider: Constants.S3_PROVIDER_LIST[0],
Region: Constants.S3_REGION_PROVIDER_REGION[0],
SecretKey: '',
@@ -47,7 +47,7 @@ export default connect(
mapStateToProps,
mapDispatchToProps
)(
class extends Component {
class AddMount extends Component {
state = {
...default_state,
};

View File

@@ -1,6 +1,7 @@
import React from 'react';
import './Configuration.css';
import { connect } from 'react-redux';
import { createDismissDisplay } from '../../utils.jsx';
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import ConfigurationItem from './ConfigurationItem/ConfigurationItem';
@@ -35,6 +36,27 @@ class Configuration extends IPCContainer {
}
return itemA.value.filter((i) => !itemB.value.includes(i)).length !== 0;
}
if (itemA.type === 'host_list') {
if (itemA.value.length !== itemB.value.length) {
return true;
}
return (
itemA.value.filter((i) =>
itemB.value.find(
(j) =>
j.HostNameOrIp === i.HostNameOrIp &&
j.ApiPort === i.ApiPort &&
j.Protocol === i.Protocol &&
j.TimeoutMs === i.TimeoutMs &&
j.AgentString === i.AgentString &&
j.ApiPassword === i.ApiPassword &&
j.AuthURL === i.AuthURL &&
j.AuthUser === i.AuthUser &&
j.AuthPassword === i.AuthPassword
)
).length === 0
);
}
return itemA.value !== itemB.value;
};
@@ -115,9 +137,11 @@ class Configuration extends IPCContainer {
remote: template[key] ? template[key].remote : false,
type: template[key] ? template[key].type : null,
value:
template[key] && template[key].type === 'object'
template[key] &&
(template[key].type === 'string_array' ||
template[key].type === 'object')
? config[key]
: template[key] && template[key].type === 'string_array'
: template[key] && template[key].type === 'host_list'
? config[key]
: config[key].toString(),
};
@@ -141,6 +165,8 @@ class Configuration extends IPCContainer {
itemList[idx].value =
target.type === 'textarea'
? target.string_array
: target.type === 'host_list'
? target.value
: target.value.toString();
this.setState({
ItemList: itemList,
@@ -156,6 +182,8 @@ class Configuration extends IPCContainer {
itemList[idx].value =
target.type === 'textarea'
? target.string_array
: target.type === 'host_list'
? target.value
: target.value.toString();
objectLookup[name] = itemList;
this.setState({
@@ -246,7 +274,11 @@ class Configuration extends IPCContainer {
changedItems.push({
Name: item.label,
Value:
item.type === 'string_array' ? item.value.join(';') : item.value,
item.type === 'string_array'
? item.value.join(';')
: item.type === 'host_list'
? JSON.stringify(item.value)
: item.value,
});
}
@@ -258,6 +290,8 @@ class Configuration extends IPCContainer {
Value:
item.type === 'string_array'
? item.value.join(';')
: item.type === 'host_list'
? JSON.stringify(item.value)
: item.value,
});
}
@@ -402,23 +436,8 @@ class Configuration extends IPCContainer {
return (
<div className={'Configuration'}>
{confirmSave}
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<div
style={{
float: 'right',
margin: 0,
padding: 0,
marginTop: '-4px',
boxSizing: 'border-box',
display: 'block',
}}>
<a
href={'#'}
onClick={this.checkSaveRequired}
style={{ cursor: 'pointer' }}>
X
</a>
</div>
<Box dxDark dxStyle={{ padding: '5px' }}>
{createDismissDisplay(this.checkSaveRequired)}
<h1 style={{ width: '100%', textAlign: 'center' }}>
{(this.props.DisplayRemoteConfiguration
? this.props.DisplayConfiguration.substr(6)

View File

@@ -7,6 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { notifyError, notifyInfo } from '../../../redux/actions/error_actions';
import settings from '../../../assets/settings';
import DropDown from '../../../components/UI/DropDown/DropDown';
import HostList from '../../HostList/HostList';
import Password from '../../../containers/UI/Password/Password';
const mapDispatchToProps = (dispatch) => {
@@ -80,6 +81,25 @@ export default connect(
);
break;
case 'host_list':
data = (
<HostList
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={(items) =>
handleChanged({
target: {
type: 'host_list',
value: items,
},
})
}
type={props.template.subtype}
value={props.value}
/>
);
break;
case 'list':
data = (
<DropDown

View File

View File

@@ -0,0 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { addEditHost } from '../../../redux/actions/host_actions';
import { connect } from 'react-redux';
import { faTrashAlt, faEdit } from '@fortawesome/free-solid-svg-icons';
// const mapStateToProps = (state) => {
// return {};
// };
//
const mapDispatchToProps = (dispatch) => {
return {
addEditHost: (host_data, cb) => dispatch(addEditHost(true, host_data, cb)),
};
};
const Host = ({ allowDelete, addEditHost, value, onChange, onDelete }) => {
const handleEditHost = () => {
addEditHost(value, (changed, host_data) => {
if (changed) {
onChange(host_data);
}
});
};
return (
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div
style={{
flex: 0,
paddingRight: 'calc(var(--default_spacing) * 1.25)',
}}>
<a href={'#'} onClick={handleEditHost}>
<FontAwesomeIcon icon={faEdit} />
</a>
</div>
{allowDelete ? (
<div
style={{
flex: 0,
paddingRight: 'calc(var(--default_spacing) * 1.25)',
}}>
<a href={'#'} onClick={onDelete}>
<FontAwesomeIcon icon={faTrashAlt} />
</a>
</div>
) : null}
<p>
{value.HostNameOrIp +
((value.Protocol === 'http' && value.ApiPort === 80) ||
(value.Protocol === 'https' && value.ApiPort === 443)
? ''
: ':' + value.ApiPort)}
</p>
</div>
);
};
Host.propTypes = {
allowDelete: PropTypes.bool.isRequired,
addEditHost: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
value: PropTypes.object.isRequired,
};
export default connect(null, mapDispatchToProps)(Host);
// export default Host;

View File

View File

@@ -0,0 +1,122 @@
import React from 'react';
import './HostList.css';
import Host from './Host/Host';
import PropTypes from 'prop-types';
import { Component } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { confirmYesNo } from '../../redux/actions/common_actions';
import { connect } from 'react-redux';
import { addEditHost } from '../../redux/actions/host_actions';
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons';
class HostList extends Component {
state = {
items: [],
};
// autoFocus={props.autoFocus}
// disabled={props.readOnly}
// type={props.template.subtype}
componentDidMount() {
this.setState({ items: this.props.value });
}
componentWillUnmount() {}
checkDuplicates = () => {
return false;
};
handleAddHost = () => {
this.props.addEditHost((changed, host_data) => {
if (changed) {
const items = [...this.state.items, host_data];
this.updateItems(items);
}
});
};
handleChanged = (host_data, index) => {
const items = [...this.state.items];
items[index] = host_data;
this.updateItems(items);
};
handleDeleted = (index) => {
this.props.ConfirmRemoveHost(
'Delete [' + this.state.items[index].HostNameOrIp + ']?',
(confirmed) => {
if (confirmed) {
const items = [...this.state.items];
items.splice(index, 1);
this.updateItems(items);
}
}
);
};
updateItems = (items) => {
this.setState(
{
items,
},
() => {
this.props.onChange(this.state.items);
}
);
};
render() {
let idx = 0;
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div
style={{
maxHeight: '80px',
maxWidth: '260px',
minWidth: '260px',
backgroundColor: 'var(--control_background)',
padding: 'var(--default_spacing)',
overflowY: 'scroll',
}}>
{this.state.items.map((v, index) => {
return (
<Host
key={idx++}
onChange={(host_data) => this.handleChanged(host_data, index)}
onDelete={() => this.handleDeleted(index)}
allowDelete={this.state.items.length > 1}
value={v}
/>
);
})}
</div>
<a
href={'#'}
onClick={this.handleAddHost}
style={{
marginTop: 'var(--default_spacing)',
}}>
<FontAwesomeIcon icon={faPlusCircle} />
<b>{' Add Portal '}</b>
</a>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
addEditHost: (cb) => dispatch(addEditHost(true, null, cb)),
ConfirmRemoveHost: (title, cb) => dispatch(confirmYesNo(title, cb)),
};
};
HostList.propTypes = {
addEditHost: PropTypes.func.isRequired,
ConfirmRemoveHost: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
value: PropTypes.array.isRequired,
};
export default connect(null, mapDispatchToProps)(HostList);

View File

@@ -1,15 +1,15 @@
import React from 'react';
import './PinnedManager.css';
import { connect } from 'react-redux';
import IPCContainer from '../IPCContainer/IPCContainer';
import { notifyApplicationBusy } from '../../redux/actions/common_actions';
import { notifyError, notifyInfo } from '../../redux/actions/error_actions';
import Box from '../../components/UI/Box/Box';
import { displayPinnedManager } from '../../redux/actions/pinned_manager_actions';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFolder } from '@fortawesome/free-solid-svg-icons';
import Button from '../../components/UI/Button/Button';
import CheckBox from '../../components/UI/CheckBox/CheckBox';
import IPCContainer from '../IPCContainer/IPCContainer';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { connect } from 'react-redux';
import { displayPinnedManager } from '../../redux/actions/pinned_manager_actions';
import { faFolder } from '@fortawesome/free-solid-svg-icons';
import { notifyApplicationBusy } from '../../redux/actions/common_actions';
import { notifyError, notifyInfo } from '../../redux/actions/error_actions';
const Constants = require('../../constants');

View File

@@ -6,11 +6,15 @@ import { getIPCRenderer } from '../../utils.jsx';
const ipcRenderer = getIPCRenderer();
let yesNoResolvers = [];
export const confirmYesNo = (title) => {
export const confirmYesNo = (title, cb) => {
return (dispatch) => {
if (cb) {
dispatch(confirmYesNo(title)).then((confirmed) => cb(confirmed));
} else {
return new Promise((resolve) => {
dispatch(handleConfirmYesNo(true, title, resolve));
});
}
};
};
@@ -116,7 +120,7 @@ export const setRebootRequired = () => {
};
export const showWindow = () => {
return (_) => {
return () => {
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Show_Window);
}

View File

@@ -57,13 +57,13 @@ export const downloadItem = (
case Constants.INSTALL_TYPES.Upgrade:
// const info =
// this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]];
const sha256 = null; // info.sha256;
const signature = null; // info.sig;
//const sha256 = null; // info.sha256;
//const signature = null; // info.sig;
dispatch(
installUpgrade(
result.Destination,
sha256,
signature,
null,
null,
!!result.SkipVerification
)
);

View File

@@ -0,0 +1,41 @@
let addEditHostResolvers = [];
export const addEditHost = (display, host_data, cb) => {
return (dispatch) => {
if (cb) {
dispatch(addEditHost(display, host_data)).then(({ changed, host_data }) =>
cb(changed, host_data)
);
} else {
return new Promise((resolve) => {
dispatch(handleDisplayAddEditHost(display, host_data, resolve));
});
}
};
};
const handleDisplayAddEditHost = (display, host_data, resolve) => {
return (dispatch) => {
if (display) {
addEditHostResolvers.push(resolve);
dispatch(displayAddEditHost(display, host_data));
} else {
dispatch(completeAddEditHost(false));
}
};
};
export const completeAddEditHost = (changed, host_data) => {
return (dispatch) => {
if (changed) {
addEditHostResolvers[0]({ changed, host_data });
}
addEditHostResolvers.splice(0, 1);
dispatch(displayAddEditHost(false));
};
};
export const DISPLAY_ADD_EDIT_HOST = 'host/displayAddEditHost';
export const displayAddEditHost = (display, host_data) => {
return { type: DISPLAY_ADD_EDIT_HOST, payload: { display, host_data } };
};

View File

@@ -31,7 +31,7 @@ export const clearUIUpgrade = () => {
};
const cleanupOldReleases = (versionList) => {
return (_) => {
return () => {
const ipcRenderer = getIPCRenderer();
if (ipcRenderer) {
ipcRenderer.sendSync(Constants.IPC_Cleanup_Releases + '_sync', {

View File

@@ -0,0 +1,20 @@
import { createReducer } from '@reduxjs/toolkit';
import * as Actions from '../actions/host_actions';
export const hostReducer = createReducer(
{
DisplayAddEditHost: false,
Edit: false,
HostData: {},
},
{
[Actions.DISPLAY_ADD_EDIT_HOST]: (state, action) => {
return {
...state,
DisplayAddEditHost: action.payload.display,
Edit: !!action.payload.host_data,
HostData: action.payload.host_data || {},
};
},
}
);

View File

@@ -8,12 +8,14 @@ import { createMountReducer } from '../reducers/mount_reducer';
import { pinnedManagerReducer } from '../reducers/pinned_manager_reducer';
import { releaseVersionReducer } from '../reducers/release_version_reducer';
import { skynetReducer } from '../reducers/skynet_reducer';
import { hostReducer } from '../reducers/host_reducer';
export default function createAppStore(platformInfo, version, state) {
const reducer = {
common: createCommonReducer(platformInfo, version),
download: downloadReducer,
error: errorReducer,
host: hostReducer,
install: installReducer,
mounts: createMountReducer(state),
relver: releaseVersionReducer,

View File

@@ -24,6 +24,30 @@ export const checkNewReleases = (selectedVersion) => {
return [];
};
export const createDismissDisplay = (closeHandler, preventClick) => {
return (
<div
style={{
float: 'right',
margin: '0px',
paddingRight: '4px',
boxSizing: 'border-box',
display: 'block',
}}>
<a
href={'#'}
onClick={(e) => {
preventClick ? e.preventDefault() : closeHandler();
}}
style={{
cursor: preventClick ? 'no-drop' : 'pointer',
}}>
X
</a>
</div>
);
};
export const createModalConditionally = (
condition,
jsx,