Prettier support

This commit is contained in:
2021-03-10 21:14:32 -06:00
parent c51b527707
commit 0924490a6f
99 changed files with 5504 additions and 3979 deletions

6
.prettierrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}

View File

@@ -1,14 +1,30 @@
{
"cSpell.words": [
"APPIMAGE",
"APPPLATFORM",
"Filebase",
"HKCC",
"HKCR",
"HKCU",
"HKEY",
"HKLM",
"LOCALAPPDATA",
"Redistributable",
"Skylinks",
"Skynet",
"Unmount",
"WINFSP",
"blockstorage",
"centos",
"msiexec",
"norestart",
"randomstring",
"reduxjs",
"relver",
"scprime",
"siaprime",
"skylink"
"skylink",
"undocked",
"windir"
]
}

View File

@@ -1,153 +1,175 @@
# Changelog
## 1.3.4
* \#55: Enable 'Activate' release instead of 'Install' in notification pop-up
- \#55: Enable 'Activate' release instead of 'Install' in notification pop-up
## 1.3.3
* \#49: Download progress is not visible if dependencies are missing
* \#51: javascript error
* \#52: Mount location is not set error on new install
* \#53: Busy notification is still visible when 'Install' button is available
* \#54: Unable to download UI update while dependencies are being checked
* Disabled 'Install' button in new release notification
- \#49: Download progress is not visible if dependencies are missing
- \#51: javascript error
- \#52: Mount location is not set error on new install
- \#53: Busy notification is still visible when 'Install' button is available
- \#54: Unable to download UI update while dependencies are being checked
- Disabled 'Install' button in new release notification
## 1.3.2
* \#48: Support pinning files to cache
* Fixed Skynet export display
* Properly detect existing remote
* Reduced number of Linux binaries to:
* CentOS 7
* Solus
* S3 mount support [disabled]
- \#48: Support pinning files to cache
- Fixed Skynet export display
- Properly detect existing remote
- Reduced number of Linux binaries to:
- CentOS 7
- Solus
- S3 mount support [disabled]
## 1.3.1
* \#45: Skynet mount support
- \#45: Skynet mount support
## 1.3.0
* \#38: Enhance new repertory release available notification
* \#46: Fix Mount Manager unmount and mount detection
* Skynet support
* Synchronize UI major/minor version with `repertory` major/minor version
* Added `Password` component
* Reduced number of Linux binaries to:
* CentOS 7
* Debian 9
* Debian 10
* Solus
* Added `FocusTrap` to modals
- \#38: Enhance new repertory release available notification
- \#46: Fix Mount Manager unmount and mount detection
- Skynet support
- Synchronize UI major/minor version with `repertory` major/minor version
- Added `Password` component
- Reduced number of Linux binaries to:
- CentOS 7
- Debian 9
- Debian 10
- Solus
- Added `FocusTrap` to modals
## 1.1.4
* \#39: Cleanup old releases and UI upgrades
- \#39: Cleanup old releases and UI upgrades
## 1.1.3
* CentOS 8 support
* Support remote send and receive timeouts
* Support WinFSP mount manager
* Fix `spawn()` failure on Windows when paths contain spaces
- CentOS 8 support
- Support remote send and receive timeouts
- Support WinFSP mount manager
- Fix `spawn()` failure on Windows when paths contain spaces
## 1.1.2
* Style changes
- Style changes
## 1.1.1
* \#43: Support for remote UNIX mounts
* Improved mount state detection
- \#43: Support for remote UNIX mounts
- Improved mount state detection
## 1.1.0
* \#40: Support for remote Windows mounts
* \#42: Failing to unmount on OS X
* Allow enabling dev tools (Ctrl-Shift-I or F12)
* ScPrime re-brand
* Ubuntu 19.10 support
* Linux Mint 19.2 support
* Fedora 31 support
- \#40: Support for remote Windows mounts
- \#42: Failing to unmount on OS X
- Allow enabling dev tools (Ctrl-Shift-I or F12)
- ScPrime re-brand
- Ubuntu 19.10 support
- Linux Mint 19.2 support
- Fedora 31 support
## 1.0.11 (Linux only)
* Fix Ubuntu 19.10 detection
- Fix Ubuntu 19.10 detection
## 1.0.10
* Fix VC runtime detection (detect additional GUID's)
- Fix VC runtime detection (detect additional GUID's)
## 1.0.9 (Alpha Testing Release)
* \#40: Support for remote Windows mounts
* ScPrime re-brand
* Fix VC runtime detection (detect additional GUID's)
- \#40: Support for remote Windows mounts
- ScPrime re-brand
- Fix VC runtime detection (detect additional GUID's)
## 1.0.8
* \#8: Add tooltips to settings
* \#36: Add ability to select Linux distribution type if OS is unsupported
* \#37: Version check fails with incorrect message when VC runtime is missing
* Added additional WinFsp uninstall strings
- \#8: Add tooltips to settings
- \#36: Add ability to select Linux distribution type if OS is unsupported
- \#37: Version check fails with incorrect message when VC runtime is missing
- Added additional WinFsp uninstall strings
## 1.0.7
* \#31: New installation displays 'Mount location is not set' on Windows
* \#33: Add 'Microsoft Visual C++ Redistributable' as dependency installation on Windows
* \#32: Don't display network error message when check for UI updates fails
* \#30: Add uninstall feature with reboot to handle WinFSP upgrades/downgrades
* \#34: Allow cancelling/closing dependency installation if version count > 1
* Handle incorrect download sizes for dependencies and releases
- \#31: New installation displays 'Mount location is not set' on Windows
- \#33: Add 'Microsoft Visual C++ Redistributable' as dependency installation on Windows
- \#32: Don't display network error message when check for UI updates fails
- \#30: Add uninstall feature with reboot to handle WinFSP upgrades/downgrades
- \#34: Allow cancelling/closing dependency installation if version count > 1
- Handle incorrect download sizes for dependencies and releases
## 1.0.6
* Additional Linux distribution support:
* Antergos
* Manjaro
* Download latest `detect_linux.sh` if bundled script returns `unknown`
* Display error message if OS is detected as `unknown`
- Additional Linux distribution support:
- Antergos
- Manjaro
- Download latest `detect_linux.sh` if bundled script returns `unknown`
- Display error message if OS is detected as `unknown`
## 1.0.5
* \#29: Mounts aren't being detected properly when switching releases
* Display window when dependencies are missing
* Display window when UI upgrade is available
* Display window and unmount all drives if release is no longer available
* Will primarily affect pre-release versions (Alpha, Beta, and RC)
- \#29: Mounts aren't being detected properly when switching releases
- Display window when dependencies are missing
- Display window when UI upgrade is available
- Display window and unmount all drives if release is no longer available
- Will primarily affect pre-release versions (Alpha, Beta, and RC)
## 1.0.4
* \#27: Implement Bitbucket backup download location
* \#28: Fix Linux upgrade
* Additional Linux distribution support:
* Debian 10
* OpenSUSE Leap 15.0
* OpenSUSE Leap 15.1
* OpenSUSE Tumbleweed
- \#27: Implement Bitbucket backup download location
- \#28: Fix Linux upgrade
- Additional Linux distribution support:
- Debian 10
- OpenSUSE Leap 15.0
- OpenSUSE Leap 15.1
- OpenSUSE Tumbleweed
## 1.0.3
* Linux distribution support
* Arch Linux
* Bodhi 5.0.0
* CentOS 7
* Debian 9
* Elementary OS 5.0
* Fedora 28
* Fedora 29
* Fedora 30
* Linux Mint 19
* Linux Mint 19.1
* Solus
* Ubuntu 18.04
* Ubuntu 18.10
* Ubuntu 19.04
* Removed `react-css-modules` dependency
* Removed Hyperspace (no active development/insufficient host network)
* Restore main window on error message
- Linux distribution support
- Arch Linux
- Bodhi 5.0.0
- CentOS 7
- Debian 9
- Elementary OS 5.0
- Fedora 28
- Fedora 29
- Fedora 30
- Linux Mint 19
- Linux Mint 19.1
- Solus
- Ubuntu 18.04
- Ubuntu 18.10
- Ubuntu 19.04
- Removed `react-css-modules` dependency
- Removed Hyperspace (no active development/insufficient host network)
- Restore main window on error message
## 1.0.2
* Option to launch application hidden (notification icon only)
* Close window to notification area
* Unmount on application exit
* Ability to cancel mount retry on unexpected failure
* OS X support
* SiaPrime support
* Partial Linux support
* Electron to v4
* Prevent mount if dependencies are missing
- Option to launch application hidden (notification icon only)
- Close window to notification area
- Unmount on application exit
- Ability to cancel mount retry on unexpected failure
- OS X support
- SiaPrime support
- Partial Linux support
- Electron to v4
- Prevent mount if dependencies are missing
## 1.0.1
* Added configuration settings for Repertory 1.0.0-alpha.2 and above
* Fixed memory leak on component unmount
* Added error display
* Lighter tray icon on Windows
* Tray icon indicates mount status on Windows
* Various fixes/layout changes
- Added configuration settings for Repertory 1.0.0-alpha.2 and above
- Fixed memory leak on component unmount
- Added error display
- Lighter tray icon on Windows
- Tray icon indicates mount status on Windows
- Various fixes/layout changes
## 1.0.0
* Initial release
* Windows support
- Initial release
- Windows support

View File

@@ -1,4 +1,5 @@
# Repertory UI
* Lars Floe <lars@krankajen.se>
* Oleg Nypadymka <onypadymka@gmail.com>
* Scott E. Graves <scott.e.graves@protonmail.com>
- Lars Floe <lars@krankajen.se>
- Oleg Nypadymka <onypadymka@gmail.com>
- Scott E. Graves <scott.e.graves@protonmail.com>

View File

@@ -5,7 +5,7 @@ const {
ipcMain,
Menu,
nativeImage,
Tray
Tray,
} = require('electron');
const AutoLaunch = require('auto-launch');
const Constants = require('../src/constants');
@@ -44,12 +44,12 @@ const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC');
const platform = os.platform();
const dimensions = {
height: (platform === 'win32') ? 326 : (platform === 'darwin') ? 322 : 300,
width: (platform === 'win32') ? 468 : 628,
height: platform === 'win32' ? 326 : platform === 'darwin' ? 322 : 300,
width: platform === 'win32' ? 468 : 628,
};
let isShutdown = false;
let isQuiting = false;
let isQuitting = false;
let isInstalling = false;
let launchHidden = false;
let cleanupReleases = false;
@@ -88,7 +88,7 @@ const createWindow = () => {
if (platform === 'linux') {
extra = {
icon: path.join(__dirname, '../build/', 'logo.png'),
}
};
}
// Create the browser window.
@@ -102,27 +102,29 @@ const createWindow = () => {
...extra,
webPreferences: {
nodeIntegration: true,
webSecurity: !process.env.ELECTRON_START_URL
}
webSecurity: !process.env.ELECTRON_START_URL,
},
});
if (platform === 'linux') {
mainWindow.setMenuBarVisibility(false);
} else {
mainWindow.removeMenu();
}
if ((platform === 'darwin') && launchHidden) {
if (platform === 'darwin' && launchHidden) {
app.dock.hide();
}
// and load the index.html of the app.
const startUrl = process.env.ELECTRON_START_URL || url.format({
const startUrl =
process.env.ELECTRON_START_URL ||
url.format({
pathname: path.join(__dirname, '../build/index.html'),
protocol: 'file:',
slashes: true
slashes: true,
});
mainWindow.on('close', function (event) {
if (!isQuiting) {
if (!isQuitting) {
event.preventDefault();
if (mainWindow.isVisible()) {
setWindowVisibility(false);
@@ -140,9 +142,14 @@ const createWindow = () => {
MountsIPC.unmountAllDrives();
});
const appPath = (platform === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) :
(platform === 'darwin') ? path.resolve(path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')) :
process.env.APPIMAGE;
const appPath =
platform === 'win32'
? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe'))
: platform === 'darwin'
? path.resolve(
path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')
)
: process.env.APPIMAGE;
const autoLauncher = new AutoLaunch({
name: 'Repertory UI',
@@ -151,51 +158,59 @@ const createWindow = () => {
trayContextMenu = Menu.buildFromTemplate([
{
label: 'Visible', type: 'checkbox', click(item) {
label: 'Visible',
type: 'checkbox',
click(item) {
setWindowVisibility(item.checked);
},
checked: !launchHidden,
},
{ type: 'separator' },
{
type: 'separator'
},
{
label: 'Auto-start', type: 'checkbox', click(item) {
label: 'Auto-start',
type: 'checkbox',
click(item) {
if (item.checked) {
autoLauncher.enable();
} else {
autoLauncher.disable();
}
}
},
},
{
label: 'Launch Hidden', type: 'checkbox', click(item) {
label: 'Launch Hidden',
type: 'checkbox',
click(item) {
launchHidden = !!item.checked;
saveUiSettings();
},
checked: launchHidden,
},
{ type: 'separator' },
{
type: 'separator'
},
{
label: 'Delete Old Releases', type: 'checkbox', click(item) {
label: 'Delete Old Releases',
type: 'checkbox',
click(item) {
cleanupReleases = !!item.checked;
saveUiSettings();
},
checked: cleanupReleases,
},
{ type: 'separator' },
{
type: 'separator'
},
{
label: 'Exit and Unmount', click() {
label: 'Exit and Unmount',
click() {
closeApplication();
}
}
},
},
]);
const image = nativeImage.createFromPath(path.join(__dirname, (platform === 'darwin') ? '../build/logo_mac.png' : '../build/logo.png'));
const image = nativeImage.createFromPath(
path.join(
__dirname,
platform === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png'
)
);
mainWindowTray = new Tray(image);
autoLauncher
@@ -203,7 +218,7 @@ const createWindow = () => {
.then((enabled) => {
trayContextMenu.items[2].checked = enabled;
mainWindowTray.setToolTip('Repertory UI');
mainWindowTray.setContextMenu(trayContextMenu)
mainWindowTray.setContextMenu(trayContextMenu);
})
.catch(() => {
closeApplication();
@@ -217,7 +232,10 @@ const getMainWindow = () => {
};
const loadUiSettings = () => {
const settingFile = path.join(helpers.resolvePath(Constants.DATA_LOCATIONS[platform]), 'ui.json');
const settingFile = path.join(
helpers.resolvePath(Constants.DATA_LOCATIONS[platform]),
'ui.json'
);
try {
if (fs.statSync(settingFile).isFile()) {
const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8'));
@@ -225,37 +243,51 @@ const loadUiSettings = () => {
cleanupReleases = !!settings.cleanup_releases;
PlatformIPC.setPlatformOverride(settings.platform_override);
}
} catch (e) {
}
} catch (e) {}
};
const saveUiSettings = () => {
const settingFile = path.join(helpers.getDataDirectory(), 'ui.json');
try {
fs.writeFileSync(settingFile, JSON.stringify({
fs.writeFileSync(
settingFile,
JSON.stringify({
cleanup_releases: cleanupReleases,
launch_hidden: launchHidden,
platform_override: PlatformIPC.getPlatformOverride(),
}), 'utf-8');
} catch (e) {
}
}),
'utf-8'
);
} catch (e) {}
};
const setIsInstalling = installing => {
const setIsInstalling = (installing) => {
isInstalling = installing;
};
const setTrayImage = driveInUse => {
const setTrayImage = (driveInUse) => {
let image;
if (driveInUse) {
image = nativeImage.createFromPath(path.join(__dirname, platform === 'darwin' ? '../build/logo_both_mac.png' : '../build/logo_both.png'));
image = nativeImage.createFromPath(
path.join(
__dirname,
platform === 'darwin'
? '../build/logo_both_mac.png'
: '../build/logo_both.png'
)
);
} else {
image = nativeImage.createFromPath(path.join(__dirname, platform === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png'));
image = nativeImage.createFromPath(
path.join(
__dirname,
platform === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png'
)
);
}
mainWindowTray.setImage(image);
};
const setWindowVisibility = show => {
const setWindowVisibility = (show) => {
if (show) {
mainWindow.show();
if (platform === 'darwin') {
@@ -277,7 +309,7 @@ const setWindowVisibility = show => {
if (trayContextMenu && mainWindowTray) {
trayContextMenu.items[0].checked = show;
mainWindowTray.setContextMenu(trayContextMenu)
mainWindowTray.setContextMenu(trayContextMenu);
}
};
@@ -288,13 +320,13 @@ const standardIPCReply = (event, channel, data, error) => {
...data,
Error: error instanceof Error ? error.toString() : error,
Success: !error,
}
},
});
}
};
app.on('before-quit', function () {
isQuiting = true;
isQuitting = true;
});
let instanceLock = app.requestSingleInstanceLock();

View File

@@ -1,16 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta charset="utf-8"/>
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000"/>
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Nunito:400,700" rel="stylesheet">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"/>
<link
href="https://fonts.googleapis.com/css?family=Nunito:400,700"
rel="stylesheet"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
@@ -24,9 +30,7 @@
<title>Repertory UI</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<noscript> You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.

View File

@@ -11,17 +11,17 @@ import InfoDetails from './components/InfoDetails/InfoDetails';
import IPCContainer from './containers/IPCContainer/IPCContainer';
import Loading from './components/UI/Loading/Loading';
import MountItems from './containers/MountItems/MountItems';
import NewReleases from './components/NewReleases/NewReleases';
import NewReleases from './components/NewReleases/NewReleases.jsx';
import { notifyError } from './redux/actions/error_actions';
import Reboot from './components/Reboot/Reboot';
import {
setDismissNewReleasesAvailable,
setNewReleasesAvailable
setNewReleasesAvailable,
} from './redux/actions/release_version_actions';
import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay';
import {
displaySelectAppPlatform,
saveState
saveState,
} from './redux/actions/common_actions';
import SelectAppPlatform from './containers/SelectAppPlatform/SelectAppPlatform';
import Text from './components/UI/Text/Text';
@@ -29,10 +29,10 @@ import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
import UpgradeUI from './components/UpgradeUI/UpgradeUI';
import {
loadReleases,
setDismissUIUpgrade
setDismissUIUpgrade,
} from './redux/actions/release_version_actions';
import YesNo from './components/YesNo/YesNo';
import {createModalConditionally} from './utils';
import { createModalConditionally } from './utils.jsx';
import SkynetImport from './containers/SkynetImport/SkynetImport';
import ApplicationBusy from './components/ApplicationBusy/ApplicationBusy';
import SkynetExport from './containers/SkynetExport/SkynetExport';
@@ -59,13 +59,17 @@ class App extends IPCContainer {
}
componentDidUpdate(prevProps) {
if ((prevProps.Release !== this.props.Release) ||
(prevProps.ReleaseVersion !== this.props.ReleaseVersion) ||
(prevProps.VersionLookup !== this.props.VersionLookup)) {
if (
prevProps.Release !== this.props.Release ||
prevProps.ReleaseVersion !== this.props.ReleaseVersion ||
prevProps.VersionLookup !== this.props.VersionLookup
) {
this.props.saveState();
} else if (Object.keys(this.props.ProviderState).filter(k => {
} else if (
Object.keys(this.props.ProviderState).filter((k) => {
return this.props.ProviderState[k] !== prevProps.ProviderState[k];
}).length > 0) {
}).length > 0
) {
this.props.saveState();
}
}
@@ -76,14 +80,16 @@ class App extends IPCContainer {
}
getSelectedVersion = () => {
return (this.props.ReleaseVersion === -1) ?
'unavailable' :
this.props.VersionLookup[Constants.RELEASE_TYPES[this.props.Release]][this.props.ReleaseVersion];
return this.props.ReleaseVersion === -1
? 'unavailable'
: this.props.VersionLookup[Constants.RELEASE_TYPES[this.props.Release]][
this.props.ReleaseVersion
];
};
handleUpgradeIconClicked = () => {
if (this.props.UpgradeAvailable) {
this.props.setDismissUIUpgrade(false)
this.props.setDismissUIUpgrade(false);
} else if (this.props.NewReleasesAvailable2.length > 0) {
this.props.setNewReleasesAvailable(this.props.NewReleasesAvailable2);
this.props.setDismissNewReleasesAvailable(false);
@@ -93,44 +99,54 @@ class App extends IPCContainer {
render() {
const selectedVersion = this.getSelectedVersion();
const downloadEnabled = this.props.AllowDownload &&
const downloadEnabled =
this.props.AllowDownload &&
!this.props.MountsBusy &&
!this.props.DownloadActive &&
(selectedVersion !== 'unavailable') &&
(selectedVersion !== this.props.InstalledVersion);
const missingDependencies = (this.props.MissingDependencies.length > 0) &&
this.props.AllowMount;
selectedVersion !== 'unavailable' &&
selectedVersion !== this.props.InstalledVersion;
const missingDependencies =
this.props.MissingDependencies.length > 0 && this.props.AllowMount;
const allowMount = this.props.AllowMount &&
const allowMount =
this.props.AllowMount &&
this.props.InstalledVersion !== 'none' &&
!missingDependencies &&
!this.props.InstallActive;
const remoteSupported = this.props.LocationsLookup[selectedVersion] &&
const remoteSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].supports_remote;
const s3Supported = this.props.LocationsLookup[selectedVersion] &&
const s3Supported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].s3_support;
const skynetSupported = this.props.LocationsLookup[selectedVersion] &&
const skynetSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].skynet_support;
const scPrimeSupported = this.props.LocationsLookup[selectedVersion] &&
const scPrimeSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].siaprime_support;
const siaSupported = this.props.LocationsLookup[selectedVersion] &&
const siaSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].sia_support;
const showConfig = !missingDependencies &&
const showConfig =
!missingDependencies &&
!this.props.DisplayPinnedManager &&
this.props.DisplayConfiguration &&
!this.props.RebootRequired;
const showPinnedManager = !missingDependencies &&
const showPinnedManager =
!missingDependencies &&
!this.props.RebootRequired &&
this.props.DisplayPinnedManager;
const showUpgrade = this.props.UpgradeAvailable &&
const showUpgrade =
this.props.UpgradeAvailable &&
!this.props.DisplayError &&
!showConfig &&
!this.props.DownloadActive &&
@@ -138,14 +154,16 @@ class App extends IPCContainer {
!this.props.InstallActive &&
!this.props.RebootRequired;
const showDependencies = !showUpgrade &&
const showDependencies =
!showUpgrade &&
missingDependencies &&
!this.props.DownloadActive &&
!this.props.RebootRequired &&
!this.props.DismissDependencies &&
this.props.AllowMount;
const showNewReleases = !showConfig &&
const showNewReleases =
!showConfig &&
!this.props.DisplayConfirmYesNo &&
!showDependencies &&
!this.props.DownloadActive &&
@@ -156,9 +174,10 @@ class App extends IPCContainer {
!this.props.DisplaySelectAppPlatform &&
!showUpgrade &&
!this.props.DismissNewReleasesAvailable &&
(this.props.NewReleasesAvailable.length > 0);
this.props.NewReleasesAvailable.length > 0;
const showSkynetImport = !showConfig &&
const showSkynetImport =
!showConfig &&
!showDependencies &&
!this.props.DownloadActive &&
!showNewReleases &&
@@ -167,7 +186,8 @@ class App extends IPCContainer {
!showUpgrade &&
this.props.DisplayImport;
const showSkynetExport = !showConfig &&
const showSkynetExport =
!showConfig &&
!showDependencies &&
!this.props.DownloadActive &&
!showNewReleases &&
@@ -176,66 +196,123 @@ class App extends IPCContainer {
!showUpgrade &&
this.props.DisplayExport;
const configDisplay = createModalConditionally(showConfig, <Configuration
const configDisplay = createModalConditionally(
showConfig,
<Configuration
version={selectedVersion}
s3Supported={s3Supported}
remoteSupported={remoteSupported}/>);
const pinnedManagerDisplay = createModalConditionally(showPinnedManager, <PinnedManager
version={selectedVersion}/>)
const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo/>);
const dependencyDisplay = createModalConditionally(showDependencies,
<DependencyList/>, false, this.props.InstallActive);
const downloadDisplay = createModalConditionally(this.props.DownloadActive,
<DownloadProgress/>, false, true);
const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true);
const newReleasesDisplay = createModalConditionally(showNewReleases, <NewReleases/>);
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot/>);
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform,
<SelectAppPlatform/>);
remoteSupported={remoteSupported}
/>
);
const pinnedManagerDisplay = createModalConditionally(
showPinnedManager,
<PinnedManager version={selectedVersion} />
);
const confirmDisplay = createModalConditionally(
this.props.DisplayConfirmYesNo,
<YesNo />
);
const dependencyDisplay = createModalConditionally(
showDependencies,
<DependencyList />,
false,
this.props.InstallActive
);
const downloadDisplay = createModalConditionally(
this.props.DownloadActive,
<DownloadProgress />,
false,
true
);
const errorDisplay = createModalConditionally(
this.props.DisplayError,
<ErrorDetails />,
true
);
const infoDisplay = createModalConditionally(
this.props.DisplayInfo,
<InfoDetails />,
true
);
const newReleasesDisplay = createModalConditionally(
showNewReleases,
<NewReleases />
);
const rebootDisplay = createModalConditionally(
this.props.RebootRequired,
<Reboot />
);
const selectAppPlatformDisplay = createModalConditionally(
this.props.DisplaySelectAppPlatform,
<SelectAppPlatform />
);
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI />);
const importDisplay = createModalConditionally(showSkynetImport, <SkynetImport
version={selectedVersion}/>);
const exportDisplay = createModalConditionally(showSkynetExport, <SkynetExport
version={selectedVersion}/>)
const appBusyDisplay = createModalConditionally(this.props.AppBusy,
<ApplicationBusy/>, false, true, this.props.AppBusyTransparent);
const importDisplay = createModalConditionally(
showSkynetImport,
<SkynetImport version={selectedVersion} />
);
const exportDisplay = createModalConditionally(
showSkynetExport,
<SkynetExport version={selectedVersion} />
);
const appBusyDisplay = createModalConditionally(
this.props.AppBusy,
<ApplicationBusy />,
false,
true,
this.props.AppBusyTransparent
);
let mainContent = [];
if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
mainContent = (
<Box dxStyle={{ height: '100%' }}>
<Loading />
</Box>);
</Box>
);
} else {
let key = 0;
mainContent.push((
<Box key={'md_' + key++}
dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}>
<ReleaseVersionDisplay downloadDisabled={!downloadEnabled}
version={selectedVersion}/>
mainContent.push(
<Box
key={'md_' + key++}
dxStyle={{ padding: 'var(--default_spacing)', height: 'auto' }}
>
<ReleaseVersionDisplay
downloadDisabled={!downloadEnabled}
version={selectedVersion}
/>
</Box>
));
mainContent.push(<div key={'md_' + key++}
style={{paddingTop: 'var(--default_spacing)'}}/>);
);
mainContent.push(
<div
key={'md_' + key++}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
if (allowMount) {
mainContent.push((
<Box dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}
key={'md_' + key++}>
<MountItems s3Supported={s3Supported}
mainContent.push(
<Box
dxStyle={{ padding: 'var(--default_spacing)', height: 'auto' }}
key={'md_' + key++}
>
<MountItems
s3Supported={s3Supported}
remoteSupported={remoteSupported}
scPrimeSupported={scPrimeSupported}
siaSupported={siaSupported}
skynetSupported={skynetSupported}/>
skynetSupported={skynetSupported}
/>
</Box>
));
} else if (!downloadEnabled && (selectedVersion !== 'unavailable')) {
mainContent.push((
<Box dxStyle={{padding: 'var(--default_spacing)', height: '170px'}}
key={'md_' + key++}>
);
} else if (!downloadEnabled && selectedVersion !== 'unavailable') {
mainContent.push(
<Box
dxStyle={{ padding: 'var(--default_spacing)', height: '170px' }}
key={'md_' + key++}
>
<Loading />
</Box>
));
);
}
}
@@ -245,27 +322,36 @@ class App extends IPCContainer {
<div className={'AppHeader'}>
<Box>
<Grid>
<Text col={0}
<Text
col={0}
colSpan={'remain'}
row={0}
rowSpan={'remain'}
text={'Repertory UI v' + this.props.Version}
textAlign={'center'}
type={'Heading1'}/>
type={'Heading1'}
/>
<UpgradeIcon
available={!missingDependencies && (this.props.UpgradeAvailable || (this.props.NewReleasesAvailable2.length > 0))}
newReleases={!missingDependencies && (!this.props.UpgradeAvailable && (this.props.NewReleasesAvailable2.length > 0))}
available={
!missingDependencies &&
(this.props.UpgradeAvailable ||
this.props.NewReleasesAvailable2.length > 0)
}
newReleases={
!missingDependencies &&
!this.props.UpgradeAvailable &&
this.props.NewReleasesAvailable2.length > 0
}
clicked={this.handleUpgradeIconClicked}
col={dimensions => dimensions.columns - 6}
col={(dimensions) => dimensions.columns - 6}
colSpan={5}
row={1}
rowSpan={remain => remain - 1}/>
rowSpan={(remain) => remain - 1}
/>
</Grid>
</Box>
</div>
<div className={'AppContent'}>
{mainContent}
</div>
<div className={'AppContent'}>{mainContent}</div>
</div>
{importDisplay}
{exportDisplay}
@@ -286,7 +372,7 @@ class App extends IPCContainer {
}
}
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AllowDownload: state.download.AllowDownload,
AllowMount: state.common.AllowMount,
@@ -324,15 +410,19 @@ const mapStateToProps = state => {
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
displaySelectAppPlatform: display => dispatch(displaySelectAppPlatform(display)),
displaySelectAppPlatform: (display) =>
dispatch(displaySelectAppPlatform(display)),
loadReleases: () => dispatch(loadReleases()),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
notifyError: (msg, critical, callback) =>
dispatch(notifyError(msg, critical, callback)),
saveState: () => dispatch(saveState()),
setDismissNewReleasesAvailable: dismiss => dispatch(setDismissNewReleasesAvailable(dismiss)),
setNewReleasesAvailable: items => dispatch(setNewReleasesAvailable(items)),
setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)),
setDismissNewReleasesAvailable: (dismiss) =>
dispatch(setDismissNewReleasesAvailable(dismiss)),
setNewReleasesAvailable: (items) =>
dispatch(setNewReleasesAvailable(items)),
setDismissUIUpgrade: (dismiss) => dispatch(setDismissUIUpgrade(dismiss)),
};
};

View File

@@ -9,12 +9,20 @@ const ApplicationBusy = ({title}) => {
<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)'}
type={'Heading1'}
/>
<div
style={{
paddingLeft: 'calc(50% - 16px)',
paddingTop: 'var(--default_spacing)',
}}
>
<Loader
color={'var(--heading_text_color)'}
height={32}
width={32}
type='TailSpin'/>
type="TailSpin"
/>
</div>
</Box>
);

View File

@@ -3,15 +3,16 @@ import './Dependency.css';
import { connect } from 'react-redux';
import * as Constants from '../../../constants';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AllowDownload: (state.download.DownloadType !== Constants.INSTALL_TYPES.Dependency) &&
AllowDownload:
state.download.DownloadType !== Constants.INSTALL_TYPES.Dependency &&
!state.download.DownloadActive &&
!state.install.InstallActive,
};
};
export default connect(mapStateToProps)(props => {
export default connect(mapStateToProps)((props) => {
return (
<div className={'Dependency'}>
<table width="100%">
@@ -21,11 +22,20 @@ export default connect(mapStateToProps)(props => {
<h3>{props.name}</h3>
</td>
<td>
{props.AllowDownload ?
<a href={'#'}
{props.AllowDownload ? (
<a
href={'#'}
className={'DependencyLink'}
onClick={()=>{props.onDownload(); return false;}}><u>Install</u></a> :
'Installing...'}
onClick={() => {
props.onDownload();
return false;
}}
>
<u>Install</u>
</a>
) : (
'Installing...'
)}
</td>
</tr>
</tbody>

View File

@@ -1,3 +1,2 @@
.DependencyList {
}

View File

@@ -5,10 +5,10 @@ import * as Constants from '../../constants';
import Dependency from './Dependency/Dependency';
import Box from '../UI/Box/Box';
import { downloadItem } from '../../redux/actions/download_actions';
import {extractFileNameFromURL} from '../../utils';
import { extractFileNameFromURL } from '../../utils.jsx';
import { setDismissDependencies } from '../../redux/actions/install_actions';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AllowDismissDependencies: state.relver.AllowDismissDependencies,
MissingDependencies: state.install.MissingDependencies,
@@ -17,33 +17,80 @@ const mapStateToProps = state => {
const mapDispatchToProps = (dispatch) => {
return {
downloadItem: (name, type, url, isWinFSP) => dispatch(downloadItem(name, type, url, isWinFSP)),
setDismissDependencies: dismiss => dispatch(setDismissDependencies(dismiss)),
downloadItem: (name, type, url, isWinFSP) =>
dispatch(downloadItem(name, type, url, isWinFSP)),
setDismissDependencies: (dismiss) =>
dispatch(setDismissDependencies(dismiss)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
const items = props.MissingDependencies.map((k, i) => {
return (
<Dependency key={i}
<Dependency
key={i}
name={k.display}
onDownload={()=>props.downloadItem(extractFileNameFromURL(k.download), Constants.INSTALL_TYPES.Dependency, k.download, k.is_winfsp)}/>
onDownload={() =>
props.downloadItem(
extractFileNameFromURL(k.download),
Constants.INSTALL_TYPES.Dependency,
k.download,
k.is_winfsp
)
}
/>
);
});
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
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}
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}>
<h1 style={{width: '100%', textAlign: 'center', color: 'var(--text_color_error)'}}>Missing Dependencies</h1>
<div
style={{
width: '100%',
height: 'auto',
paddingBottom: '5px',
boxSizing: 'border-box',
}}
>
<h1
style={{
width: '100%',
textAlign: 'center',
color: 'var(--text_color_error)',
}}
>
Missing Dependencies
</h1>
</div>
{items}
</Box>

View File

@@ -3,7 +3,7 @@ import {connect} from 'react-redux';
import React from 'react';
import './DownloadProgress.css';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
DownloadName: state.download.DownloadName,
DownloadProgress: state.download.DownloadProgress,
@@ -11,15 +11,21 @@ const mapStateToProps = state => {
};
};
export default connect(mapStateToProps)(props => {
export default connect(mapStateToProps)((props) => {
const width = props.Platform === 'linux' ? '480px' : '380px';
return (
<Box dxStyle={{ width: width, height: 'auto', padding: '5px' }}>
<div style={{ width: '100%', height: 'auto' }}>
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.DownloadName}</h1>
<h1 style={{ width: '100%', textAlign: 'center' }}>
{'Downloading ' + props.DownloadName}
</h1>
</div>
<progress max={100.0} id={'download_progress'}
<progress
max={100.0}
id={'download_progress'}
style={{ width: '100%' }}
value={props.DownloadProgress}/>
</Box>);
value={props.DownloadProgress}
/>
</Box>
);
});

View File

@@ -5,19 +5,23 @@ import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import './ErrorDetails.css';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
ErrorMessage: state.error.ErrorStack.length > 0 ? state.error.ErrorStack[0] : '',
ErrorMessage:
state.error.ErrorStack.length > 0 ? state.error.ErrorStack[0] : '',
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
dismissError: () => dispatch(dismissError()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
return (
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'ErrorDetailsHeading'}>Application Error</h1>

View File

@@ -5,19 +5,23 @@ import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import './InfoDetails.css';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
InfoMessage: state.error.InfoStack.length > 0 ? state.error.InfoStack[0] : '',
InfoMessage:
state.error.InfoStack.length > 0 ? state.error.InfoStack[0] : '',
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
dismissInfo: () => dispatch(dismissInfo()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
let msg = props.InfoMessage.message;
const classes = ['InfoDetailsContent'];
@@ -37,7 +41,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
}
}
const scrollToTop = textArea => {
const scrollToTop = (textArea) => {
if (!textArea.firstClick) {
textArea.firstClick = true;
textArea.scrollTop = 0;
@@ -49,17 +53,17 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1>
<div className={classes.join(' ')}>
{
copyable ? (
<textarea autoFocus
{copyable ? (
<textarea
autoFocus
rows={9}
value={msg}
className={'SkynetImportTextArea'}
onClick={e => scrollToTop(e.target)}/>
onClick={(e) => scrollToTop(e.target)}
/>
) : (
<p style={{ textAlign: 'left' }}>{msg}</p>
)
}
)}
</div>
<Button clicked={props.dismissInfo}>Dismiss</Button>
</Box>

View File

@@ -1,83 +0,0 @@
import React from 'react';
import {connect} from 'react-redux';
import * as Constants from '../../../constants';
import Button from '../../UI/Button/Button';
import {formatLinesForDisplay, getChangesForRepertoryVersion} from '../../../utils';
import {
notifyError,
notifyInfo
} from '../../../redux/actions/error_actions';
import {setActiveRelease} from '../../../redux/actions/release_version_actions';
import {unmountAll} from '../../../redux/actions/mount_actions';
const mapStateToProps = state => {
return {
ActiveRelease: state.relver.Release,
ActiveVersion: state.relver.Version,
}
};
const mapDispatchToProps = dispatch => {
return {
setActiveRelease: (release, version) => dispatch(setActiveRelease(release, version)),
notifyError: msg => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
unmountAll: completedCallback => dispatch(unmountAll(completedCallback)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(({
ActiveRelease,
ActiveVersion,
dismiss,
release,
lastItem,
notifyError,
notifyInfo,
setActiveRelease,
unmountAll,
}) => {
const title = '[' + Constants.RELEASE_TYPES[release.Release] + '] ' + release.Display;
const displayChanges = async () => {
try {
const lines = await getChangesForRepertoryVersion(release.VersionString);
notifyInfo(title, formatLinesForDisplay(lines));
} catch (e) {
notifyError(e);
}
};
const isActiveRelease = ((release.Release === ActiveRelease) && (release.Version === ActiveVersion));
const setReleaseAndVersion = () => {
dismiss();
unmountAll(() => {
setActiveRelease(release.Release, release.Version);
});
};
return (
<div>
<h2>{title}</h2>
<table cellSpacing={0} cellPadding={0} width="97%">
<tbody>
<tr style={{height: '4px'}}/>
<tr>
<td width="50%">
<Button buttonStyles={{width: '100%'}} clicked={displayChanges}>Changes</Button>
</td>
<td>
<div style={{width: 'var(--default_spacing)'}}/>
</td>
<td width="50%">
{!isActiveRelease ?
<Button buttonStyles={{width: '100%'}}
clicked={setReleaseAndVersion}>Activate</Button>
: null}
</td>
</tr>
{lastItem ? null : <tr style={{height: 'var(--default_spacing)'}}/>}
</tbody>
</table>
</div>
);
});

View File

@@ -0,0 +1,104 @@
import React from 'react';
import { connect } from 'react-redux';
import * as Constants from '../../../constants';
import Button from '../../UI/Button/Button';
import {
formatLinesForDisplay,
getChangesForRepertoryVersion,
} from '../../../utils.jsx';
import { notifyError, notifyInfo } from '../../../redux/actions/error_actions';
import { setActiveRelease } from '../../../redux/actions/release_version_actions';
import { unmountAll } from '../../../redux/actions/mount_actions';
const mapStateToProps = (state) => {
return {
ActiveRelease: state.relver.Release,
ActiveVersion: state.relver.Version,
};
};
const mapDispatchToProps = (dispatch) => {
return {
setActiveRelease: (release, version) =>
dispatch(setActiveRelease(release, version)),
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
unmountAll: (completedCallback) => dispatch(unmountAll(completedCallback)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(
({
ActiveRelease,
ActiveVersion,
dismiss,
release,
lastItem,
notifyError,
notifyInfo,
setActiveRelease,
unmountAll,
}) => {
const title =
'[' + Constants.RELEASE_TYPES[release.Release] + '] ' + release.Display;
const displayChanges = async () => {
try {
const lines = await getChangesForRepertoryVersion(
release.VersionString
);
notifyInfo(title, formatLinesForDisplay(lines));
} catch (e) {
notifyError(e);
}
};
const isActiveRelease =
release.Release === ActiveRelease && release.Version === ActiveVersion;
const setReleaseAndVersion = () => {
dismiss();
unmountAll(() => {
setActiveRelease(release.Release, release.Version);
});
};
return (
<div>
<h2>{title}</h2>
<table cellSpacing={0} cellPadding={0} width="97%">
<tbody>
<tr style={{ height: '4px' }} />
<tr>
<td width="50%">
<Button
buttonStyles={{ width: '100%' }}
clicked={displayChanges}
>
Changes
</Button>
</td>
<td>
<div style={{ width: 'var(--default_spacing)' }} />
</td>
<td width="50%">
{!isActiveRelease ? (
<Button
buttonStyles={{ width: '100%' }}
clicked={setReleaseAndVersion}
>
Activate
</Button>
) : null}
</td>
</tr>
{lastItem ? null : (
<tr style={{ height: 'var(--default_spacing)' }} />
)}
</tbody>
</table>
</div>
);
}
);

View File

@@ -1,38 +0,0 @@
import React from 'react';
import {connect} from 'react-redux';
import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import NewRelease from './NewRelease/NewRelease';
import './NewReleases.css';
import {setDismissNewReleasesAvailable} from '../../redux/actions/release_version_actions';
const mapStateToProps = state => {
return {
NewReleasesAvailable: state.relver.NewReleasesAvailable,
};
};
const mapDispatchToProps = dispatch => {
return {
dismissNewReleasesAvailable: () => dispatch(setDismissNewReleasesAvailable(true)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
const newReleases = props.NewReleasesAvailable.map((i, idx) => {
return <NewRelease dismiss={props.dismissNewReleasesAvailable}
key={'new_release_' + i.Release + '_' + i.Version}
lastItem={idx === (props.NewReleasesAvailable.length - 1)}
release={i} />;
});
return (
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'NewReleasesHeading'}>New Repertory Versions Available</h1>
<div className={'NewReleasesContent'}>
{newReleases}
</div>
<Button clicked={props.dismissNewReleasesAvailable}>Dismiss</Button>
</Box>
);
});

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { connect } from 'react-redux';
import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import NewRelease from './NewRelease/NewRelease.jsx';
import './NewReleases.css';
import { setDismissNewReleasesAvailable } from '../../redux/actions/release_version_actions';
const mapStateToProps = (state) => {
return {
NewReleasesAvailable: state.relver.NewReleasesAvailable,
};
};
const mapDispatchToProps = (dispatch) => {
return {
dismissNewReleasesAvailable: () =>
dispatch(setDismissNewReleasesAvailable(true)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
const newReleases = props.NewReleasesAvailable.map((i, idx) => {
return (
<NewRelease
dismiss={props.dismissNewReleasesAvailable}
key={'new_release_' + i.Release + '_' + i.Version}
lastItem={idx === props.NewReleasesAvailable.length - 1}
release={i}
/>
);
});
return (
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'NewReleasesHeading'}>New Repertory Versions Available</h1>
<div className={'NewReleasesContent'}>{newReleases}</div>
<Button clicked={props.dismissNewReleasesAvailable}>Dismiss</Button>
</Box>
);
});

View File

@@ -5,13 +5,16 @@ import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import { rebootSystem } from '../../redux/actions/common_actions';
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
rebootSystem: () => dispatch(rebootSystem()),
};
};
export default connect(null, mapDispatchToProps)(props => {
export default connect(
null,
mapDispatchToProps
)((props) => {
return (
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'RebootHeading'}>Reboot System</h1>

View File

@@ -10,7 +10,7 @@ import UpgradeIcon from '../UpgradeIcon/UpgradeIcon';
import { setActiveRelease } from '../../redux/actions/release_version_actions';
import { downloadItem } from '../../redux/actions/download_actions';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AllowMount: state.common.AllowMount,
AppPlatform: state.common.AppPlatform,
@@ -28,128 +28,170 @@ const mapStateToProps = state => {
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
downloadItem: (name, type, urls) => dispatch(downloadItem(name, type, urls)),
setActiveRelease: (release, version) => dispatch(setActiveRelease(release, version)),
}
downloadItem: (name, type, urls) =>
dispatch(downloadItem(name, type, urls)),
setActiveRelease: (release, version) =>
dispatch(setActiveRelease(release, version)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
const getSelectedVersion = () => {
return (props.ReleaseVersion === -1) ?
'unavailable' :
props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion];
return props.ReleaseVersion === -1
? 'unavailable'
: props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][
props.ReleaseVersion
];
};
const handleDownloadRelease = () => {
const fileName = props.version + '.zip';
props.downloadItem(fileName, Constants.INSTALL_TYPES.Release, props.LocationsLookup[props.version].urls);
props.downloadItem(
fileName,
Constants.INSTALL_TYPES.Release,
props.LocationsLookup[props.version].urls
);
};
const handleReleaseChanged = e => {
const handleReleaseChanged = (e) => {
const release = Constants.RELEASE_TYPES.indexOf(e.target.value);
const releaseVersion = props.VersionLookup[Constants.RELEASE_TYPES[release]].length - 1;
const releaseVersion =
props.VersionLookup[Constants.RELEASE_TYPES[release]].length - 1;
props.setActiveRelease(release, releaseVersion);
};
const handleVersionChanged = e => {
const releaseVersion = props.VersionLookup[Constants.RELEASE_TYPES[props.Release]].indexOf(e.target.value);
const handleVersionChanged = (e) => {
const releaseVersion = props.VersionLookup[
Constants.RELEASE_TYPES[props.Release]
].indexOf(e.target.value);
props.setActiveRelease(props.Release, releaseVersion);
};
const text = props.InstalledVersion + ' [' + props.AppPlatform + ']';
const disabled = props.DownloadActive ||
const disabled =
props.DownloadActive ||
props.InstallActive ||
props.MountsBusy ||
(!props.AllowMount && (getSelectedVersion() !== 'unavailable')) ;
const releaseExtracting = (props.InstallType === Constants.INSTALL_TYPES.Release);
(!props.AllowMount && getSelectedVersion() !== 'unavailable');
const releaseExtracting =
props.InstallType === Constants.INSTALL_TYPES.Release;
let optionsDisplay = [];
let key = 0;
if (releaseExtracting) {
optionsDisplay.push((
<Text col={dimensions => (dimensions.columns / 3) * 2}
optionsDisplay.push(
<Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'}
key={key++}
rowSpan={4}
text={'Activating'}
textAlign={'left'}
type={'Heading2'}/>
));
optionsDisplay.push((
<Text col={dimensions => (dimensions.columns / 3) * 2}
type={'Heading2'}
/>
);
optionsDisplay.push(
<Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'}
key={key++}
row={5}
rowSpan={7}
text={text}
textAlign={'left'}/>
));
textAlign={'left'}
/>
);
} else if (props.downloadDisabled || props.DismissDependencies) {
optionsDisplay.push((
<Text col={dimensions => (dimensions.columns / 3) * 2}
optionsDisplay.push(
<Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'}
key={key++}
rowSpan={4}
text={'Installed'}
textAlign={'left'}
type={'Heading2'}/>
));
type={'Heading2'}
/>
);
optionsDisplay.push((
<Text col={dimensions => (dimensions.columns / 3) * 2}
optionsDisplay.push(
<Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'}
key={key++}
row={5}
rowSpan={7}
text={text}
textAlign={'left'}/>
));
textAlign={'left'}
/>
);
} else {
optionsDisplay.push((
<Button clicked={handleDownloadRelease}
col={dimensions => (dimensions.columns / 3) * 2}
optionsDisplay.push(
<Button
clicked={handleDownloadRelease}
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={20}
key={key++}
row={5}
rowSpan={7}>Install</Button>
));
rowSpan={7}
>
Install
</Button>
);
}
return (
<Grid noScroll>
<Text colSpan={columns=>columns / 3}
<Text
colSpan={(columns) => columns / 3}
rowSpan={4}
text={'Release'}
textAlign={'left'}
type={'Heading2'}/>
<DropDown changed={handleReleaseChanged}
colSpan={remain=>remain / 3 - 1}
type={'Heading2'}
/>
<DropDown
changed={handleReleaseChanged}
colSpan={(remain) => remain / 3 - 1}
disabled={disabled}
items={Constants.RELEASE_TYPES}
row={5}
rowSpan={7}
selected={Constants.RELEASE_TYPES[props.Release]}/>
<Text col={dimensions => dimensions.columns / 3}
colSpan={remain=>remain / 2}
selected={Constants.RELEASE_TYPES[props.Release]}
/>
<Text
col={(dimensions) => dimensions.columns / 3}
colSpan={(remain) => remain / 2}
rowSpan={4}
text={'Version'}
textAlign={'left'}
type={'Heading2'}/>
<UpgradeIcon available={props.ReleaseUpgradeAvailable}
col={dimensions => ((dimensions.columns / 3) * 2) - 6}
type={'Heading2'}
/>
<UpgradeIcon
available={props.ReleaseUpgradeAvailable}
col={(dimensions) => (dimensions.columns / 3) * 2 - 6}
colSpan={4}
release
rowSpan={4}/>
<DropDown changed={handleVersionChanged}
col={dimensions => dimensions.columns / 3}
colSpan={remain=>remain / 2 - 1}
rowSpan={4}
/>
<DropDown
changed={handleVersionChanged}
col={(dimensions) => dimensions.columns / 3}
colSpan={(remain) => remain / 2 - 1}
disabled={disabled}
items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]}
row={5}
rowSpan={7}
selected={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion]}/>
selected={
props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][
props.ReleaseVersion
]
}
/>
{optionsDisplay}
</Grid>
);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import './Box.css';
const Box = props => {
const Box = (props) => {
const styleList = [];
styleList.push('Box');
if (props.dxDark) {
@@ -20,7 +20,8 @@ const Box = props => {
<div
onClick={props.clicked}
className={styleList.join(' ')}
style={{...props.dxStyle}}>
style={{ ...props.dxStyle }}
>
{props.children}
</div>
);

View File

@@ -1,13 +1,17 @@
import React from 'react';
import './Button.css';
const Button = props => {
const Button = (props) => {
return (
<button disabled={props.disabled}
<button
disabled={props.disabled}
autoFocus={props.autoFocus}
className={'Button'}
style={props.buttonStyles}
onClick={props.clicked}>{props.children}</button>
onClick={props.clicked}
>
{props.children}
</button>
);
};

View File

@@ -18,7 +18,7 @@ label.CheckBoxLabel {
}
/* Hide the browser's default checkbox */
label.CheckBoxLabel input[type=checkbox] {
label.CheckBoxLabel input[type='checkbox'] {
position: absolute;
opacity: 0;
cursor: pointer;
@@ -41,7 +41,7 @@ label.CheckBoxLabel input[type=checkbox] {
}
/* On mouse-over, add a grey background color */
label.CheckBoxLabel:hover input[type=checkbox] ~ .CheckBoxCheckMark {
label.CheckBoxLabel:hover input[type='checkbox'] ~ .CheckBoxCheckMark {
background-color: var(--control_background_hover);
}
@@ -52,7 +52,7 @@ label.CheckBoxLabel input:checked ~ .CheckBoxCheckMark {
/* Create the CheckBoxCheckMark/indicator (hidden when not checked) */
.CheckBoxCheckMark:after {
content: "";
content: '';
position: absolute;
display: none;
}

View File

@@ -1,16 +1,19 @@
import React from 'react';
import './CheckBox.css';
const CheckBox = props => {
const CheckBox = (props) => {
return (
<div className={'CheckBoxOwner'}>
<label className='CheckBoxLabel'>{props.label}
<input checked={JSON.parse(props.checked)}
<label className="CheckBoxLabel">
{props.label}
<input
checked={JSON.parse(props.checked)}
autoFocus={props.autoFocus}
disabled={props.disabled}
onChange={props.changed}
type='checkbox'/>
<span className='CheckBoxCheckMark'/>
type="checkbox"
/>
<span className="CheckBoxCheckMark" />
</label>
</div>
);

View File

@@ -6,7 +6,8 @@
padding: 0;
}
.DropDownSelect, .DropDownSelect.Alt {
.DropDownSelect,
.DropDownSelect.Alt {
width: 100%;
height: 100%;
display: block;

View File

@@ -1,26 +1,32 @@
import React from 'react';
import './DropDown.css';
const DropDown = props => {
const DropDown = (props) => {
const options = props.items.map((s, i) => {
return (
<option className={'DropDownOption'} key={i} value={s}>{s}</option>
<option className={'DropDownOption'} key={i} value={s}>
{s}
</option>
);
});
return (
<div className={'DropDown'}>
<select
className={'DropDownSelect' + (props.auto ? ' Auto ' : '') + (props.alt ? ' Alt ' : '')}
className={
'DropDownSelect' +
(props.auto ? ' Auto ' : '') +
(props.alt ? ' Alt ' : '')
}
autoFocus={props.autoFocus}
disabled={props.disabled}
onChange={props.changed}
value={props.selected}>
value={props.selected}
>
{options}
</select>
</div>
);
};
export default DropDown;

View File

@@ -10,14 +10,14 @@ export default class Grid extends Component {
calculated: false,
dimensions: {
columns: 0,
rows: 0
}
rows: 0,
},
};
calculateDimensions = size => {
calculateDimensions = (size) => {
return {
columns: Math.floor(size.width / this.getGridSize()),
rows: Math.floor(size.height / this.getGridSize())
rows: Math.floor(size.height / this.getGridSize()),
};
};
@@ -29,7 +29,7 @@ export default class Grid extends Component {
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
clearInterval(this.resizeTimeout);
};
}
getGridSize = () => {
return this.props.gridSize || DEFAULT_GRID_SIZE;
@@ -43,7 +43,7 @@ export default class Grid extends Component {
const elem = this.refs.GridOwner;
return {
height: elem ? elem.offsetHeight : 0,
width: elem ? elem.offsetWidth : 0
width: elem ? elem.offsetWidth : 0,
};
};
@@ -54,15 +54,15 @@ export default class Grid extends Component {
updateSize = () => {
const state = {
...this.state
...this.state,
};
const size = this.getSize();
const dimensions = this.calculateDimensions(size);
if (state.dimensions !== dimensions) {
this.setState({
calculated: true,
dimensions: dimensions
})
dimensions: dimensions,
});
}
};
@@ -79,34 +79,44 @@ export default class Grid extends Component {
children = React.Children.map(this.props.children, (child, i) => {
if (child) {
let row = child.props.row || 0;
if (typeof(row) === 'function') {
if (typeof row === 'function') {
row = row(dimensions);
}
let col = child.props.col || 0;
if (typeof(col) === 'function') {
if (typeof col === 'function') {
col = col(dimensions);
}
let rowSpan = child.props.rowSpan;
if (typeof(rowSpan) === 'function') {
if (typeof rowSpan === 'function') {
rowSpan = rowSpan(dimensions.rows - row, dimensions.rows);
}
let colSpan = child.props.colSpan;
if (typeof(colSpan) === 'function') {
if (typeof colSpan === 'function') {
colSpan = colSpan(dimensions.columns - col, dimensions.columns);
}
rowSpan = rowSpan ? (rowSpan === 'remain' ? (dimensions.rows - row) : rowSpan) : null;
colSpan = colSpan ? (colSpan === 'remain' ? dimensions.columns - col : colSpan) : null;
rowSpan = rowSpan
? rowSpan === 'remain'
? dimensions.rows - row
: rowSpan
: null;
colSpan = colSpan
? colSpan === 'remain'
? dimensions.columns - col
: colSpan
: null;
return (
<GridComponent row={row}
<GridComponent
row={row}
col={col}
rowSpan={rowSpan}
colSpan={colSpan}
key={'gc_' + i}>
key={'gc_' + i}
>
{child}
</GridComponent>
);
@@ -123,7 +133,7 @@ export default class Grid extends Component {
gridTemplateRows: gridSizePx.repeat(dimensions.rows),
gridAutoColumns: gridSizePx,
gridAutoRows: gridSizePx,
}
},
};
if (this.props.noScroll) {
@@ -132,13 +142,11 @@ export default class Grid extends Component {
}
return (
<div
ref='GridOwner'
className={'GridOwner'}>
<div ref="GridOwner" className={'GridOwner'}>
<div className={'Grid'} {...style}>
{children}
</div>
</div>
)
);
}
}
};

View File

@@ -1,14 +1,14 @@
import React from 'react';
import './GridComponent.css';
const GridComponent = props => {
const GridComponent = (props) => {
const style = {
style: {
gridRowStart: Math.floor(props.row + 1),
gridRowEnd: 'span ' + Math.floor(props.rowSpan || 1),
gridColumnStart: Math.floor(props.col + 1),
gridColumnEnd: 'span ' + Math.floor(props.colSpan || 1)
}
gridColumnEnd: 'span ' + Math.floor(props.colSpan || 1),
},
};
return (
@@ -16,7 +16,6 @@ const GridComponent = props => {
{props.children}
</div>
);
};
export default GridComponent;

View File

@@ -10,7 +10,8 @@
margin: 0;
padding: 0;
position: relative;
top: 50%; left: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 28px;
height: 28px;

View File

@@ -1,18 +1,20 @@
import React from 'react';
import './Loading.css'
import './Loading.css';
import Loader from 'react-loader-spinner';
const Loading = () => {
return (
<div
className={'Loading'}>
<div className={'Loading'}>
<div className={'LoadingContent'}>
<Loader color={'var(--heading_text_color)'}
<Loader
color={'var(--heading_text_color)'}
height={28}
width={28}
type='ThreeDots'/>
type="ThreeDots"
/>
</div>
</div>);
</div>
);
};
export default Loading;

View File

@@ -1,9 +1,9 @@
import React from 'react';
import './Modal.css'
import './Modal.css';
import FocusTrap from 'focus-trap-react';
const Modal = props => {
const Modal = (props) => {
let modalStyles = [];
let contentStyles = [];
modalStyles.push('Modal');
@@ -19,12 +19,8 @@ const Modal = props => {
return (
<FocusTrap active={!props.disableFocusTrap}>
<div
className={modalStyles.join(' ')}
onClick={props.clicked}>
<div className={contentStyles.join(' ')}>
{props.children}
</div>
<div className={modalStyles.join(' ')} onClick={props.clicked}>
<div className={contentStyles.join(' ')}>{props.children}</div>
</div>
</FocusTrap>
);

View File

@@ -1,11 +1,11 @@
import React from 'react';
const RootElem = props => {
const RootElem = (props) => {
return (
<div style={{ margin: 0, padding: 0 }} {...props}>
{props.children}
</div>
)
);
};
export default RootElem;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import './Text.css';
const Text = props => {
const Text = (props) => {
const styleList = [];
styleList.push('Text');
if (props.type) {
@@ -14,15 +14,12 @@ const Text = props => {
}
const text = (
<div
className={styleList.join(' ')}
style={style}>{props.text}
</div>);
<div className={styleList.join(' ')} style={style}>
{props.text}
</div>
);
return props.noOwner ? text : (
<div className={'TextOwner'}>
{text}
</div>);
return props.noOwner ? text : <div className={'TextOwner'}>{text}</div>;
};
export default Text;

View File

@@ -1,10 +1,11 @@
import React from 'react';
import './UpgradeIcon.css';
import ReactTooltip from 'react-tooltip';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import ReactTooltip from 'react-tooltip';
const UpgradeIcon = props => {
const UpgradeIcon = (props) => {
const styles = ['UpgradeIcon'];
let placement = 'left';
let toolTipText = 'UI Upgrade Available';
@@ -17,21 +18,18 @@ const UpgradeIcon = props => {
toolTipText = 'New Release Available';
}
return props
.available ?
(
return props.available ? (
<div className={'UpgradeIconOwner'}>
<p data-tip='' data-for={placement}>
<a href={'#'}
className={styles.join(' ')}
onClick={props.clicked}>
<p data-tip="" data-for={placement}>
<a href={'#'} className={styles.join(' ')} onClick={props.clicked}>
<FontAwesomeIcon icon={faExclamationTriangle} />
</a>
</p>
<ReactTooltip id={placement} place={placement}>{toolTipText}</ReactTooltip>
<ReactTooltip id={placement} place={placement}>
{toolTipText}
</ReactTooltip>
</div>
)
: null;
) : null;
};
export default UpgradeIcon;

View File

@@ -7,49 +7,66 @@ import './UpgradeUI.css';
import { setDismissUIUpgrade } from '../../redux/actions/release_version_actions';
import { downloadItem } from '../../redux/actions/download_actions';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
Platform: state.common.Platform,
UpgradeData: state.relver.UpgradeData,
UpgradeVersion: state.relver.UpgradeVersion,
}
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
downloadItem: (name, type, urls) => dispatch(downloadItem(name, type, urls)),
setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)),
downloadItem: (name, type, urls) =>
dispatch(downloadItem(name, type, urls)),
setDismissUIUpgrade: (dismiss) => dispatch(setDismissUIUpgrade(dismiss)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
const handleDownload = () => {
const name = (props.Platform === 'win32') ?
'upgrade.exe' :
(props.Platform === 'darwin') ?
'upgrade.dmg' :
'repertory-ui_' + props.UpgradeVersion + '_linux_x86_64.AppImage';
props.downloadItem(name, Constants.INSTALL_TYPES.Upgrade, props.UpgradeData.urls);
const name =
props.Platform === 'win32'
? 'upgrade.exe'
: props.Platform === 'darwin'
? 'upgrade.dmg'
: 'repertory-ui_' + props.UpgradeVersion + '_linux_x86_64.AppImage';
props.downloadItem(
name,
Constants.INSTALL_TYPES.Upgrade,
props.UpgradeData.urls
);
};
return (
<Box dxStyle={{ width: '180px', height: 'auto', padding: '5px' }}>
<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>
<table cellSpacing={5} width="100%">
<tbody>
<tr>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={handleDownload}>Install</Button>
<Button buttonStyles={{ width: '100%' }} clicked={handleDownload}>
Install
</Button>
</td>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => props.setDismissUIUpgrade(true)}>Cancel</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => props.setDismissUIUpgrade(true)}
>
Cancel
</Button>
</td>
</tr>
</tbody>
</table>
</Box>);
</Box>
);
});

View File

@@ -5,21 +5,30 @@ import React from 'react';
import './YesNo.css';
import { hideConfirmYesNo } from '../../redux/actions/common_actions';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
Title: state.common.ConfirmTitle,
}
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
hideConfirmYesNo: confirmed => dispatch(hideConfirmYesNo(confirmed)),
hideConfirmYesNo: (confirmed) => dispatch(hideConfirmYesNo(confirmed)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
return (
<Box dxStyle={{minWidth: '180px', height: 'auto', padding: 'var(--default_spacing)'}}>
<Box
dxStyle={{
minWidth: '180px',
height: 'auto',
padding: 'var(--default_spacing)',
}}
>
<div style={{ width: '100%', height: 'auto' }}>
<h1 style={{ width: '100%', textAlign: 'center' }}>{props.Title}</h1>
</div>
@@ -27,15 +36,24 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
<tbody>
<tr>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => props.hideConfirmYesNo(true)}>Yes</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => props.hideConfirmYesNo(true)}
>
Yes
</Button>
</td>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => props.hideConfirmYesNo(false)}>No</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => props.hideConfirmYesNo(false)}
>
No
</Button>
</td>
</tr>
</tbody>
</table>
</Box>);
</Box>
);
});

View File

@@ -32,19 +32,21 @@ const _REPERTORY_UI_BRANCH = '1.3.x_branch';
exports.REPERTORY_BRANCH = _REPERTORY_BRANCH;
exports.REPERTORY_UI_BRANCH = _REPERTORY_UI_BRANCH;
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' +
_REPERTORY_BRANCH + '/releases_1.3.json';
exports.RELEASES_URL =
'https://bitbucket.org/blockstorage/repertory/raw/' +
_REPERTORY_BRANCH +
'/releases_1.3.json';
exports.UI_RELEASES_URL =
'https://bitbucket.org/blockstorage/repertory-ui/raw/' +
_REPERTORY_UI_BRANCH + '/releases.json';
_REPERTORY_UI_BRANCH +
'/releases.json';
exports.LINUX_DETECT_SCRIPT_URL =
'https://bitbucket.org/blockstorage/repertory/raw/' + _REPERTORY_BRANCH +
'https://bitbucket.org/blockstorage/repertory/raw/' +
_REPERTORY_BRANCH +
'/detect_linux2.sh';
exports.LINUX_SELECTABLE_PLATFORMS = [
'centos7',
];
exports.LINUX_SELECTABLE_PLATFORMS = ['centos7'];
exports.WINFSP_VERSION_NAMES = [
'WinFsp 2019',
@@ -70,32 +72,24 @@ exports.WINFSP_VERSION_NAMES = [
exports.DATA_LOCATIONS = {
linux: '~/.local/repertory/ui',
darwin: '~/Library/Application Support/repertory/ui',
win32 : '%LOCALAPPDATA%\\repertory\\ui'
win32: '%LOCALAPPDATA%\\repertory\\ui',
};
exports.REPERTORY_LOCATIONS = {
linux: '~/.local/repertory',
darwin: '~/Library/Application Support/repertory',
win32 : '%LOCALAPPDATA%\\repertory'
win32: '%LOCALAPPDATA%\\repertory',
};
exports.S3_PROVIDER_LIST = [
'Filebase',
];
exports.S3_PROVIDER_LIST = ['Filebase'];
exports.S3_REGION_PROVIDER_REGION = [
'us-east-1',
];
exports.S3_REGION_PROVIDER_REGION = ['us-east-1'];
exports.S3_PROVIDER_URL = {
'Filebase' : 'https://s3.filebase.com',
Filebase: 'https://s3.filebase.com',
};
exports.PROVIDER_LIST = [
'Sia',
'Skynet',
'ScPrime',
];
exports.PROVIDER_LIST = ['Sia', 'Skynet', 'ScPrime'];
exports.PROVIDER_ARG = {
sia: '',
@@ -105,12 +99,7 @@ exports.PROVIDER_ARG = {
};
exports.DEFAULT_RELEASE = 0;
exports.RELEASE_TYPES = [
'Release',
'RC',
'Beta',
'Alpha',
];
exports.RELEASE_TYPES = ['Release', 'RC', 'Beta', 'Alpha'];
exports.INSTALL_TYPES = {
Dependency: 'dependency',

View File

@@ -7,23 +7,26 @@ import Box from '../../components/UI/Box/Box';
import Text from '../../components/UI/Text/Text';
import { notifyError } from '../../redux/actions/error_actions';
import { addRemoteMount, addS3Mount } from '../../redux/actions/mount_actions';
import {createModalConditionally} from '../../utils';
import { createModalConditionally } from '../../utils.jsx';
import DropDown from '../../components/UI/DropDown/DropDown';
import * as Constants from '../../constants';
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
RemoteMounts: state.mounts.RemoteMounts,
S3Mounts: state.mounts.S3Mounts,
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
addRemoteMount: (hostNameOrIp, port, token) => dispatch(addRemoteMount(hostNameOrIp, port, token)),
addS3Mount: (name, accessKey, secretKey, region, bucketName, url) => dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName, url)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
}
addRemoteMount: (hostNameOrIp, port, token) =>
dispatch(addRemoteMount(hostNameOrIp, port, token)),
addS3Mount: (name, accessKey, secretKey, region, bucketName, url) =>
dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName, url)),
notifyError: (msg, critical, callback) =>
dispatch(notifyError(msg, critical, callback)),
};
};
const default_state = {
@@ -40,7 +43,11 @@ const default_state = {
Token: '',
};
export default connect(mapStateToProps, mapDispatchToProps)(class extends Component {
export default connect(
mapStateToProps,
mapDispatchToProps
)(
class extends Component {
state = {
...default_state,
};
@@ -49,18 +56,26 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
if (this.state.HostNameOrIp.length === 0) {
this.props.notifyError('Hostname or IP cannot be empty.');
} else {
const provider = 'Remote' + this.state.HostNameOrIp + ':' + this.state.Port;
const provider =
'Remote' + this.state.HostNameOrIp + ':' + this.state.Port;
if (this.props.RemoteMounts.includes(provider)) {
this.props.notifyError('Remote host already exists');
} else {
this.setState({
DisplayRemote: false
}, () => {
this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token);
this.setState(
{
DisplayRemote: false,
},
() => {
this.props.addRemoteMount(
this.state.HostNameOrIp,
this.state.Port,
this.state.Token
);
this.setState({
...default_state,
});
});
}
);
}
}
};
@@ -71,21 +86,30 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
} else if (this.state.AccessKey.length === 0) {
this.props.notifyError('AccessKey cannot be empty.');
} else if (this.state.SecretKey.length === 0) {
this.props.notifyError('SecretKey cannot be empty.')
this.props.notifyError('SecretKey cannot be empty.');
} else {
const provider = 'S3' + this.state.Name;
if (this.props.S3Mounts.includes(provider)) {
this.props.notifyError('Remote host already exists');
} else {
this.setState({
DisplayS3: false
}, () => {
this.props.addS3Mount(this.state.Name, this.state.AccessKey, this.state.SecretKey,
this.state.Region, this.state.BucketName, Constants.S3_PROVIDER_URL[this.state.Provider]);
this.setState(
{
DisplayS3: false,
},
() => {
this.props.addS3Mount(
this.state.Name,
this.state.AccessKey,
this.state.SecretKey,
this.state.Region,
this.state.BucketName,
Constants.S3_PROVIDER_URL[this.state.Provider]
);
this.setState({
...default_state,
});
});
}
);
}
}
};
@@ -105,142 +129,202 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
};
render() {
const displayAddRemote = createModalConditionally(this.state.DisplayRemote, (
<Box dxDark
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add Remote
Mount</h1>
<Text text={'Hostname or IP'}
textAlign={'left'}
type={'Heading2'}/>
<input onChange={e => this.setState({HostNameOrIp: e.target.value.trim()})}
const displayAddRemote = createModalConditionally(
this.state.DisplayRemote,
<Box
dxDark
dxStyle={{
width: 'auto',
height: 'auto',
padding: 'var(--default_spacing)',
}}
>
<h1
style={{
textAlign: 'center',
paddingBottom: 'var(--default_spacing)',
}}
>
Add Remote Mount
</h1>
<Text text={'Hostname or IP'} textAlign={'left'} type={'Heading2'} />
<input
onChange={(e) =>
this.setState({ HostNameOrIp: e.target.value.trim() })
}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.HostNameOrIp}/>
value={this.state.HostNameOrIp}
/>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<Text text={'Port'}
textAlign={'left'}
type={'Heading2'}/>
<input max={65535}
<Text text={'Port'} textAlign={'left'} type={'Heading2'} />
<input
max={65535}
min={1025}
onChange={e => this.setState({Port: e.target.value})}
onChange={(e) => this.setState({ Port: e.target.value })}
className={'ConfigurationItemInput'}
type={'number'}
value={this.state.Port}/>
value={this.state.Port}
/>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<Text text={'Remote Token'}
textAlign={'left'}
type={'Heading2'}/>
<input onChange={e => this.setState({Token: e.target.value})}
<Text text={'Remote Token'} textAlign={'left'} type={'Heading2'} />
<input
onChange={(e) => this.setState({ Token: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Token}/>
value={this.state.Token}
/>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Button buttonStyles={{width: '100%'}}
clicked={() => this.addRemoteMount()}>OK</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.addRemoteMount()}
>
OK
</Button>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({DisplayRemote: false})}>Cancel</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.setState({ DisplayRemote: false })}
>
Cancel
</Button>
</div>
</Box>
));
);
const displayAddS3 = createModalConditionally(this.state.DisplayS3, (
<Box dxDark
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add S3
Mount</h1>
const displayAddS3 = createModalConditionally(
this.state.DisplayS3,
<Box
dxDark
dxStyle={{
width: 'auto',
height: 'auto',
padding: 'var(--default_spacing)',
}}
>
<h1
style={{
textAlign: 'center',
paddingBottom: 'var(--default_spacing)',
}}
>
Add S3 Mount
</h1>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Name'}
textAlign={'left'}
type={'Heading2'}/>
<Text text={'Name'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Provider'}
textAlign={'left'}
type={'Heading2'}/>
<Text text={'Provider'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input onChange={e => this.setState({Name: e.target.value.trim()})}
<input
onChange={(e) => this.setState({ Name: e.target.value.trim() })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.Name}/>
value={this.state.Name}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<DropDown changed={e => this.setState({Provider: e.target.value})}
<DropDown
changed={(e) => this.setState({ Provider: e.target.value })}
items={Constants.S3_PROVIDER_LIST}
selected={this.state.Provider}/>
selected={this.state.Provider}
/>
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Bucket Name (optional)'}
<Text
text={'Bucket Name (optional)'}
textAlign={'left'}
type={'Heading2'}/>
type={'Heading2'}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Region'}
textAlign={'left'}
type={'Heading2'}/>
<Text text={'Region'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input onChange={e => this.setState({BucketName: e.target.value})}
<input
onChange={(e) => this.setState({ BucketName: e.target.value })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.BucketName}/>
value={this.state.BucketName}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<input onChange={e => this.setState({Region: e.target.value})}
<input
onChange={(e) => this.setState({ Region: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Region}/>
value={this.state.Region}
/>
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Access Key'}
textAlign={'left'}
type={'Heading2'}/>
<Text text={'Access Key'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Secret Key'}
textAlign={'left'}
type={'Heading2'}/>
<Text text={'Secret Key'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input onChange={e => this.setState({AccessKey: e.target.value})}
<input
onChange={(e) => this.setState({ AccessKey: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.AccessKey}/>
value={this.state.AccessKey}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<input onChange={e => this.setState({SecretKey: e.target.value})}
<input
onChange={(e) => this.setState({ SecretKey: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.SecretKey}/>
value={this.state.SecretKey}
/>
</div>
<div style={{ paddingTop: 'calc(var(--default_spacing) * 2)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div style={{ width: '200%' }} />
<Button buttonStyles={{width: '100%'}}
clicked={() => this.addS3Mount()}>OK</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.addS3Mount()}
>
OK
</Button>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({DisplayS3: false})}>Cancel</Button>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.setState({ DisplayS3: false })}
>
Cancel
</Button>
</div>
</Box>
));
);
return (
<div className={'AddMount'}>
{displayAddRemote}
{displayAddS3}
<div className={'AddMountButtons'}>
{this.props.remoteSupported ?
<Button className={'AddMountButton'}
clicked={this.handleAddRemoteMount}>Add Remote Mount</Button> : null}
{this.props.remoteSupported && this.props.s3Supported ?
<div style={{paddingRight: 'var(--default_spacing)'}}/> : null}
{this.props.s3Supported ?
<Button className={'AddMountButton'}
clicked={this.handleAddS3Mount}>Add S3 Mount</Button> : null}
{this.props.remoteSupported ? (
<Button
className={'AddMountButton'}
clicked={this.handleAddRemoteMount}
>
Add Remote Mount
</Button>
) : null}
{this.props.remoteSupported && this.props.s3Supported ? (
<div style={{ paddingRight: 'var(--default_spacing)' }} />
) : null}
{this.props.s3Supported ? (
<Button
className={'AddMountButton'}
clicked={this.handleAddS3Mount}
>
Add S3 Mount
</Button>
) : null}
</div>
</div>
);
}
});
}
);

View File

@@ -25,7 +25,7 @@ class Configuration extends IPCContainer {
ItemList: [],
Saving: false,
ShowAdvanced: false,
Template: {}
Template: {},
};
checkItemChanged = (itemA, itemB) => {
@@ -33,7 +33,7 @@ class Configuration extends IPCContainer {
if (itemA.value.length !== itemB.value.length) {
return true;
}
return itemA.value.filter(i => !itemB.value.includes(i)).length !== 0;
return itemA.value.filter((i) => !itemB.value.includes(i)).length !== 0;
}
return itemA.value !== itemB.value;
};
@@ -52,7 +52,9 @@ class Configuration extends IPCContainer {
const changedObjectItems = [];
let j = 0;
for (const item of this.state.ObjectLookup[key]) {
if (this.checkItemChanged(this.state.OriginalObjectLookup[key][j++], item)) {
if (
this.checkItemChanged(this.state.OriginalObjectLookup[key][j++], item)
) {
changedObjectItems.push(item);
}
}
@@ -65,7 +67,7 @@ class Configuration extends IPCContainer {
}
}
if ((changedItems.length > 0) || changedObjectLookup) {
if (changedItems.length > 0 || changedObjectLookup) {
this.setState({
ChangedItems: changedItems,
ChangedObjectLookup: changedObjectLookup,
@@ -77,9 +79,18 @@ class Configuration extends IPCContainer {
componentDidMount() {
this._isMounted = true;
this.setRequestHandler(Constants.IPC_Get_Config_Template_Reply, this.onGetConfigTemplateReply);
this.setRequestHandler(Constants.IPC_Get_Config_Reply, this.onGetConfigReply);
this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply);
this.setRequestHandler(
Constants.IPC_Get_Config_Template_Reply,
this.onGetConfigTemplateReply
);
this.setRequestHandler(
Constants.IPC_Get_Config_Reply,
this.onGetConfigReply
);
this.setRequestHandler(
Constants.IPC_Set_Config_Values_Reply,
this.onSetConfigValuesReply
);
this.sendRequest(Constants.IPC_Get_Config_Template, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
@@ -95,25 +106,25 @@ class Configuration extends IPCContainer {
createItemList = (config, template) => {
const objectList = [];
const itemList = Object
.keys(config)
.map(key => {
const itemList = Object.keys(config)
.map((key) => {
return {
advanced: template[key] ? template[key].advanced : false,
hide_remote: template[key] ? template[key].hide_remote : false,
label: key,
remote: template[key] ? template[key].remote : false,
type: template[key] ? template[key].type : null,
value: (template[key] && (template[key].type === 'object')) ?
config[key] :
(template[key] && (template[key].type === 'string_array')) ?
config[key] :
config[key].toString(),
value:
template[key] && template[key].type === 'object'
? config[key]
: template[key] && template[key].type === 'string_array'
? config[key]
: config[key].toString(),
};
})
.filter(i => {
.filter((i) => {
let ret = template[i.label];
if (ret && (template[i.label].type === 'object')) {
if (ret && template[i.label].type === 'object') {
objectList.push(i);
ret = false;
}
@@ -122,28 +133,30 @@ class Configuration extends IPCContainer {
return {
ObjectList: objectList,
ItemList: itemList,
}
};
};
handleItemChanged = (target, idx) => {
const itemList = [
...this.state.ItemList
];
itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString();
const itemList = [...this.state.ItemList];
itemList[idx].value =
target.type === 'textarea'
? target.string_array
: target.value.toString();
this.setState({
ItemList: itemList
ItemList: itemList,
});
};
handleObjectItemChanged = (target, name, idx) => {
const itemList = [
...this.state.ObjectLookup[name]
];
const itemList = [...this.state.ObjectLookup[name]];
const objectLookup = {
...this.state.ObjectLookup,
};
itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString();
itemList[idx].value =
target.type === 'textarea'
? target.string_array
: target.value.toString();
objectLookup[name] = itemList;
this.setState({
ObjectLookup: objectLookup,
@@ -157,12 +170,19 @@ class Configuration extends IPCContainer {
let objectLookup = {};
for (const obj of list.ObjectList) {
const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template);
const list2 = this.createItemList(
obj.value,
this.state.Template[obj.label].template
);
objectLookup[obj.label] = list2.ItemList;
}
const isRemoteMount = this.props.remoteSupported &&
JSON.parse(objectLookup['RemoteMount'].find(s => s.label === 'IsRemoteMount').value);
const isRemoteMount =
this.props.remoteSupported &&
JSON.parse(
objectLookup['RemoteMount'].find((s) => s.label === 'IsRemoteMount')
.value
);
if (isRemoteMount) {
for (const obj of list.ObjectList) {
if (obj.hide_remote) {
@@ -172,15 +192,16 @@ class Configuration extends IPCContainer {
}
const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
this.setState({
this.setState(
{
IsRemoteMount: isRemoteMount,
ItemList: list.ItemList,
ObjectLookup: objectLookup,
OriginalItemList: itemListCopy,
OriginalObjectLookup: objectLookupCopy,
}, () => {
});
},
() => {}
);
} else {
this.props.notifyError(arg.data.Error);
}
@@ -188,16 +209,19 @@ class Configuration extends IPCContainer {
onGetConfigTemplateReply = (event, arg) => {
if (arg.data.Success) {
this.setState({
this.setState(
{
Template: arg.data.Template,
}, () => {
},
() => {
this.sendRequest(Constants.IPC_Get_Config, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
S3: this.props.DisplayS3Configuration,
Version: this.props.version,
});
});
}
);
} else {
this.props.notifyError(arg.data.Error, false, () => {
if (this._isMounted) {
@@ -212,16 +236,17 @@ class Configuration extends IPCContainer {
};
saveAndClose = () => {
this.setState({
this.setState(
{
Saving: true,
}, () => {
},
() => {
const changedItems = [];
for (const item of this.state.ChangedItems) {
changedItems.push({
Name: item.label,
Value: item.type === 'string_array' ?
item.value.join(';') :
item.value,
Value:
item.type === 'string_array' ? item.value.join(';') : item.value,
});
}
@@ -230,9 +255,10 @@ class Configuration extends IPCContainer {
for (const item of this.state.ChangedObjectLookup[key]) {
changedItems.push({
Name: key + '.' + item.label,
Value: item.type === 'string_array' ?
item.value.join(';') :
item.value,
Value:
item.type === 'string_array'
? item.value.join(';')
: item.value,
});
}
}
@@ -245,43 +271,66 @@ class Configuration extends IPCContainer {
S3: this.props.DisplayS3Configuration,
Version: this.props.version,
});
});
}
);
};
showRemoteConfigItem = (item, itemList) => {
if (item.advanced &&
if (
item.advanced &&
item.remote &&
this.props.remoteSupported &&
(item.label !== 'IsRemoteMount')) {
const isRemoteMount = JSON.parse(itemList.find(s => s.label === 'IsRemoteMount').value);
const enableRemoteMount = !isRemoteMount &&
JSON.parse(itemList.find(s => s.label === 'EnableRemoteMount').value);
return (item.label === 'RemoteHostNameOrIp') || (item.label === 'RemoteMaxConnections') ?
isRemoteMount :
(item.label === 'RemoteReceiveTimeoutSeconds') || (item.label === 'RemoteSendTimeoutSeconds') || (item.label === 'RemotePort') || (item.label === 'RemoteToken') ?
isRemoteMount || enableRemoteMount :
(item.label === 'EnableRemoteMount') ?
!isRemoteMount :
enableRemoteMount;
item.label !== 'IsRemoteMount'
) {
const isRemoteMount = JSON.parse(
itemList.find((s) => s.label === 'IsRemoteMount').value
);
const enableRemoteMount =
!isRemoteMount &&
JSON.parse(itemList.find((s) => s.label === 'EnableRemoteMount').value);
return item.label === 'RemoteHostNameOrIp' ||
item.label === 'RemoteMaxConnections'
? isRemoteMount
: item.label === 'RemoteReceiveTimeoutSeconds' ||
item.label === 'RemoteSendTimeoutSeconds' ||
item.label === 'RemotePort' ||
item.label === 'RemoteToken'
? isRemoteMount || enableRemoteMount
: item.label === 'EnableRemoteMount'
? !isRemoteMount
: enableRemoteMount;
}
return false;
};
render() {
let confirmSave = null;
if ((this.state.ChangedItems.length > 0) || this.state.ChangedObjectLookup) {
if (this.state.ChangedItems.length > 0 || this.state.ChangedObjectLookup) {
confirmSave = (
<Modal>
<Box dxStyle={{ width: '40vw', padding: 'var(--default_spacing)' }}>
<h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1>
<table width='100%'>
<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 align="center" width="50%">
<Button
clicked={this.saveAndClose}
disabled={this.state.Saving}
>
Yes
</Button>
</td>
<td align="center" width="50%">
<Button
clicked={this.props.hideConfiguration}
disabled={this.state.Saving}
>
No
</Button>
</td>
<td align='center' width='50%'><Button clicked={this.props.hideConfiguration}
disabled={this.state.Saving}>No</Button></td>
</tr>
</tbody>
</table>
@@ -294,84 +343,105 @@ class Configuration extends IPCContainer {
let objectItems = [];
for (const key of Object.keys(this.state.ObjectLookup)) {
objectItems.push((
objectItems.push(
<div key={key}>
<h2>{key}</h2>
<div>
{
this.state.ObjectLookup[key].map((k, i) => {
{this.state.ObjectLookup[key].map((k, i) => {
const shouldFocus = autoFocus;
autoFocus = false;
return (
(!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ?
<ConfigurationItem advanced={k.advanced}
return !k.advanced ||
(this.state.ShowAdvanced && k.advanced && !k.remote) ||
this.showRemoteConfigItem(k, this.state.ObjectLookup[key]) ? (
<ConfigurationItem
advanced={k.advanced}
autoFocus={shouldFocus}
changed={e => this.handleObjectItemChanged(e, key, i)}
changed={(e) => this.handleObjectItemChanged(e, key, i)}
grouping={key}
items={this.state.Template[key].template[k.label].items}
key={i}
label={k.label}
readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))}
template={this.state.Template[key].template[k.label]}
value={k.value}/> :
null)
})
readOnly={
this.state.IsRemoteMount &&
(k.label === 'RemoteHostNameOrIp' ||
k.label === 'RemotePort')
}
template={this.state.Template[key].template[k.label]}
value={k.value}
/>
) : null;
})}
</div>
</div>
));
);
}
const configurationItems = this.state.ItemList
.map((k, i) => {
const configurationItems = this.state.ItemList.map((k, i) => {
const shouldFocus = autoFocus;
autoFocus = false;
return (
((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
<ConfigurationItem advanced={k.advanced}
return (!this.state.IsRemoteMount || !k.hide_remote) &&
(!k.advanced || (this.state.ShowAdvanced && k.advanced)) ? (
<ConfigurationItem
advanced={k.advanced}
autoFocus={shouldFocus}
changed={e => this.handleItemChanged(e, i)}
changed={(e) => this.handleItemChanged(e, i)}
grouping={'Settings'}
items={this.state.Template[k.label].items}
key={i}
label={k.label}
template={this.state.Template[k.label]}
value={k.value}/> :
null
);
value={k.value}
/>
) : null;
});
return (
<div className={'Configuration'}>
{confirmSave}
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<div style={{
<div
style={{
float: 'right',
margin: 0,
padding: 0,
marginTop: '-4px',
boxSizing: 'border-box',
display: 'block'
}}>
<a href={'#'}
display: 'block',
}}
>
<a
href={'#'}
onClick={this.checkSaveRequired}
style={{cursor: 'pointer'}}>X</a>
style={{ cursor: 'pointer' }}
>
X
</a>
</div>
<h1 style={{width: '100%', textAlign: 'center'}}>{(
this.props.DisplayRemoteConfiguration ?
this.props.DisplayConfiguration.substr(6) :
this.props.DisplayConfiguration) + ' Configuration '}
<h1 style={{ width: '100%', textAlign: 'center' }}>
{(this.props.DisplayRemoteConfiguration
? this.props.DisplayConfiguration.substr(6)
: this.props.DisplayConfiguration) + ' Configuration '}
</h1>
<div style={{ overflowY: 'auto', height: '90%' }}>
{this.props.MState.Mounted && (configurationItems.length > 0) ? <Button
buttonStyles={{width: 'auto', height: 'auto', marginLeft: 'auto', marginRight: '4px'}}
{this.props.MState.Mounted && configurationItems.length > 0 ? (
<Button
buttonStyles={{
width: 'auto',
height: 'auto',
marginLeft: 'auto',
marginRight: '4px',
}}
clicked={() => {
this.props.displayPinnedManager(true);
return false;
}}>&nbsp;Pinned File Manager...&nbsp;</Button> : null}
}}
>
&nbsp;Pinned File Manager...&nbsp;
</Button>
) : null}
<div style={{ marginBottom: '4px' }} />
{objectItems}
{(configurationItems.length > 0) ? <h2>Settings</h2> : null}
{configurationItems.length > 0 ? <h2>Settings</h2> : null}
{configurationItems}
</div>
</Box>
@@ -380,22 +450,23 @@ class Configuration extends IPCContainer {
}
}
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
DisplayS3Configuration: state.mounts.DisplayS3Configuration,
MState: state.mounts.MountState[state.mounts.DisplayConfiguration],
Platform: state.common.Platform,
}
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
displayPinnedManager: display => dispatch(displayPinnedManager(display)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
displayPinnedManager: (display) => dispatch(displayPinnedManager(display)),
notifyError: (msg, critical, callback) =>
dispatch(notifyError(msg, critical, callback)),
hideConfiguration: () => dispatch(displayConfiguration(null, false)),
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Configuration);

View File

@@ -4,28 +4,30 @@ import CheckBox from '../../../components/UI/CheckBox/CheckBox';
import { connect } from 'react-redux';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
notifyError,
notifyInfo
} from '../../../redux/actions/error_actions';
import { notifyError, notifyInfo } from '../../../redux/actions/error_actions';
import settings from '../../../assets/settings';
import DropDown from '../../../components/UI/DropDown/DropDown';
import Password from '../../../containers/UI/Password/Password';
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
notifyError: msg => dispatch(notifyError(msg)),
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
}
};
};
export default connect(null, mapDispatchToProps)(props => {
export default connect(
null,
mapDispatchToProps
)((props) => {
const handleChanged = (e) => {
const target = e.target;
if (target.type === 'checkbox') {
target.value = e.target.checked ? 'true' : 'false';
} else if (target.type === 'textarea') {
e.target.string_array = String(e.target.value).replace(/\r\n/g,'\n').split('\n');
e.target.string_array = String(e.target.value)
.replace(/\r\n/g, '\n')
.split('\n');
}
props.changed(target);
};
@@ -37,135 +39,184 @@ export default connect(null, mapDispatchToProps)(props => {
props.notifyInfo(props.label, description);
};
infoDisplay = <a href={'#'}
infoDisplay = (
<a
href={'#'}
className={'ConfigurationInfo'}
onClick={()=>{displayInfo(); return false;}}><FontAwesomeIcon icon={faInfoCircle}/></a>;
onClick={() => {
displayInfo();
return false;
}}
>
<FontAwesomeIcon icon={faInfoCircle} />
</a>
);
}
let data;
switch (props.template.type) {
case 'bool':
data = <CheckBox changed={handleChanged}
data = (
<CheckBox
changed={handleChanged}
checked={props.value}
disabled={props.readOnly}
autoFocus={props.autoFocus}/>;
autoFocus={props.autoFocus}
/>
);
break;
case 'double':
data = <input min={0.0}
data = (
<input
min={0.0}
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
onChange={(e) => handleChanged(e)}
step={'0.01'}
className={'ConfigurationItemInput'}
type={'number'}
value={parseFloat(props.value).toFixed(2)}/>;
value={parseFloat(props.value).toFixed(2)}
/>
);
break;
case 'list':
data = <DropDown alt
data = (
<DropDown
alt
auto
autoFocus={props.autoFocus}
changed={handleChanged}
disabled={props.readOnly}
items={props.items}
selected={props.value} />;
selected={props.value}
/>
);
break;
case 'string':
if (props.template.subtype === 'password') {
data = (
<Password autoFocus={props.autoFocus}
changed={s => handleChanged({
<Password
autoFocus={props.autoFocus}
changed={(s) =>
handleChanged({
target: {
type: 'password',
value: s,
},
})}
})
}
disabled={props.readOnly}
mismatchHandler={() => props.notifyError('Passwords do not match')}
value={props.value} />
value={props.value}
/>
);
} else {
data = (
<input onChange={e => handleChanged(e)}
<input
onChange={(e) => handleChanged(e)}
autoFocus={props.autoFocus}
className={'ConfigurationItemInput'}
disabled={props.readOnly}
type={'text'}
value={props.value}/>
value={props.value}
/>
);
}
break;
case 'uint8':
data = <input max={255}
data = (
<input
max={255}
min={0}
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
value={props.value}/>;
value={props.value}
/>
);
break;
case 'uint16':
data = <input max={65535}
data = (
<input
max={65535}
min={0}
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
value={props.value}/>;
value={props.value}
/>
);
break;
case 'uint32':
data = <input max={4294967295}
data = (
<input
max={4294967295}
min={0}
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
value={props.value}/>;
value={props.value}
/>
);
break;
case 'uint64':
data = <input max={18446744073709551615}
data = (
<input
max={18446744073709551615}
min={0}
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
value={props.value}/>;
value={props.value}
/>
);
break;
case 'string_array':
data = (
<textarea autoFocus={props.autoFocus}
<textarea
autoFocus={props.autoFocus}
disabled={props.readOnly}
rows={4}
cols={36}
onChange={e=>handleChanged(e)}
onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'}
value={props.value.join('\n')} />
value={props.value.join('\n')}
/>
);
break;
case 'password':
data = (
<Password autoFocus={props.autoFocus}
changed={s => handleChanged({
<Password
autoFocus={props.autoFocus}
changed={(s) =>
handleChanged({
target: {
type: 'password',
value: s,
},
})}
})
}
disabled={props.readOnly}
mismatchHandler={() => props.notifyError('Passwords do not match')}
value={props.value} />
value={props.value}
/>
);
break;
@@ -175,13 +226,18 @@ export default connect(null, mapDispatchToProps)(props => {
return (
<div className={'ConfigurationItem'}>
<table cellPadding='2'
width='100%'>
<table cellPadding="2" width="100%">
<tbody>
<tr>
{infoDisplay ?
<td width='100%' valign={'top'}>{infoDisplay} {props.label}</td> :
<td width='100%' valign={'top'}>{props.label}</td>}
{infoDisplay ? (
<td width="100%" valign={'top'}>
{infoDisplay} {props.label}
</td>
) : (
<td width="100%" valign={'top'}>
{props.label}
</td>
)}
<td>{data}</td>
</tr>
</tbody>

View File

@@ -1,5 +1,5 @@
import { Component } from 'react';
import {getIPCRenderer} from '../../utils';
import { getIPCRenderer } from '../../utils.jsx';
const ipcRenderer = getIPCRenderer();
@@ -16,7 +16,7 @@ export default class IPCContainer extends Component {
}
this.handlerList = {};
};
}
sendRequest = (name, data) => {
if (ipcRenderer) {
@@ -41,5 +41,4 @@ export default class IPCContainer extends Component {
ipcRenderer.on(name, callback);
}
};
};
}

View File

@@ -11,12 +11,12 @@ import RootElem from '../../../components/UI/RootElem/RootElem';
import {
displayConfiguration,
removeMount,
setProviderState
setProviderState,
} from '../../../redux/actions/mount_actions';
import {
displaySkynetExport,
displaySkynetImport,
} from '../../../redux/actions/skynet_actions'
} from '../../../redux/actions/skynet_actions';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import CheckBox from '../../../components/UI/CheckBox/CheckBox';
@@ -25,22 +25,27 @@ const mapStateToProps = (state, ownProps) => {
return {
MState: state.mounts.MountState[ownProps.provider],
Platform: state.common.Platform,
PState: state.mounts.ProviderState[ownProps.provider]
PState: state.mounts.ProviderState[ownProps.provider],
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
displayConfiguration: (provider, remote, s3) => dispatch(displayConfiguration(provider, remote, s3)),
displaySkynetExport: display => dispatch(displaySkynetExport(display)),
displaySkynetImport: display => dispatch(displaySkynetImport(display)),
removeMount: provider => dispatch(removeMount(provider)),
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
}
displayConfiguration: (provider, remote, s3) =>
dispatch(displayConfiguration(provider, remote, s3)),
displaySkynetExport: (display) => dispatch(displaySkynetExport(display)),
displaySkynetImport: (display) => dispatch(displaySkynetImport(display)),
removeMount: (provider) => dispatch(removeMount(provider)),
setProviderState: (provider, state) =>
dispatch(setProviderState(provider, state)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
const handleAutoMountChanged = e => {
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
const handleAutoMountChanged = (e) => {
const state = {
...props.PState,
AutoMount: e.target.checked,
@@ -48,7 +53,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
props.setProviderState(props.provider, state);
};
const handleAutoRestartChanged = e => {
const handleAutoRestartChanged = (e) => {
const state = {
...props.PState,
AutoRestart: e.target.checked,
@@ -59,16 +64,26 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
let secondRow = 6;
const pointer = { cursor: props.MState.AllowMount ? 'pointer' : 'no-drop' };
const configButton = (
<RootElem colSpan={4}
rowSpan={6}>
<img alt=''
<RootElem colSpan={4} rowSpan={6}>
<img
alt=""
height={'16px'}
onClick={props.MState.AllowMount ? () => props.displayConfiguration(props.provider, props.remote, props.s3) : e => {
onClick={
props.MState.AllowMount
? () =>
props.displayConfiguration(
props.provider,
props.remote,
props.s3
)
: (e) => {
e.preventDefault();
}}
}
}
src={configureImage}
style={{ padding: 0, border: 0, margin: 0, ...pointer }}
width={'16px'}/>
width={'16px'}
/>
</RootElem>
);
@@ -77,80 +92,119 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
if (props.Platform === 'win32') {
inputColumnSpan = 20;
const index = props.MState.DriveLetters.indexOf(props.PState.MountLocation);
inputControls = <DropDown changed={props.changed}
inputControls = (
<DropDown
changed={props.changed}
colSpan={inputColumnSpan}
disabled={!props.MState.AllowMount || props.MState.Mounted}
items={props.MState.DriveLetters}
row={secondRow}
rowSpan={7}
selected={index >= 0 ? props.PState.MountLocation : ''}/>;
selected={index >= 0 ? props.PState.MountLocation : ''}
/>
);
} else {
inputColumnSpan = 64;
inputControls = [];
let key = 0;
inputControls.push((
<RootElem colSpan={inputColumnSpan - 8}
inputControls.push(
<RootElem
colSpan={inputColumnSpan - 8}
key={'i' + key++}
row={secondRow}
rowSpan={7}>
<input disabled={!props.MState.AllowMount || props.MState.Mounted}
rowSpan={7}
>
<input
disabled={!props.MState.AllowMount || props.MState.Mounted}
maxLength={4096}
onChange={props.changed}
size={4096}
className={'MountItemInput'}
type={'text'}
value={props.PState.MountLocation}/>
value={props.PState.MountLocation}
/>
</RootElem>
));
inputControls.push((
<Button clicked={()=>props.browseClicked(props.provider, props.PState.MountLocation)}
);
inputControls.push(
<Button
clicked={() =>
props.browseClicked(props.provider, props.PState.MountLocation)
}
col={inputColumnSpan - 7}
colSpan={7}
disabled={props.MState.Mounted || !props.MState.AllowMount}
key={'b' + key++}
row={secondRow}
rowSpan={7}>...</Button>
));
rowSpan={7}
>
...
</Button>
);
}
const buttonDisplay = props.MState.AllowMount ?
(props.MState.Mounted ? 'Unmount' : 'Mount') :
<Loader color={'var(--heading_text_color)'}
const buttonDisplay = props.MState.AllowMount ? (
props.MState.Mounted ? (
'Unmount'
) : (
'Mount'
)
) : (
<Loader
color={'var(--heading_text_color)'}
height={19}
type='Circles'
width={19}/>;
type="Circles"
width={19}
/>
);
const actionsDisplay = (
<Button
clicked={() => props.clicked(props.provider, props.remote, props.s3, !props.MState.Mounted, props.PState.MountLocation)}
clicked={() =>
props.clicked(
props.provider,
props.remote,
props.s3,
!props.MState.Mounted,
props.PState.MountLocation
)
}
col={inputColumnSpan + 2}
colSpan={21}
disabled={!props.MState.AllowMount}
row={secondRow}
rowSpan={7}>
rowSpan={7}
>
{buttonDisplay}
</Button>);
</Button>
);
const autoMountControl = (
<RootElem col={inputColumnSpan + 24}
<RootElem
col={inputColumnSpan + 24}
colSpan={28}
row={secondRow}
rowSpan={7}>
<CheckBox changed={handleAutoMountChanged}
rowSpan={7}
>
<CheckBox
changed={handleAutoMountChanged}
checked={props.PState.AutoMount}
label={'Auto-mount'}/>
label={'Auto-mount'}
/>
</RootElem>
);
const autoRestartControl = (
<RootElem col={inputColumnSpan + 24 + 28}
<RootElem
col={inputColumnSpan + 24 + 28}
colSpan={24}
row={secondRow}
rowSpan={7}>
<CheckBox changed={handleAutoRestartChanged}
rowSpan={7}
>
<CheckBox
changed={handleAutoRestartChanged}
checked={props.PState.AutoRestart}
label={'Restart'}/>
label={'Restart'}
/>
</RootElem>
);
@@ -158,7 +212,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
if (props.allowRemove) {
const removeDisabled = !props.MState.AllowMount || props.MState.Mounted;
const removeStyle = {
cursor: removeDisabled ? 'no-drop' : 'pointer'
cursor: removeDisabled ? 'no-drop' : 'pointer',
};
const handleRemoveMount = () => {
if (!removeDisabled) {
@@ -166,17 +220,18 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
}
};
removeControl = (
<RootElem col={dimensions=>dimensions.columns - 6}
row={secondRow + 3}>
<a href={'#'}
onClick={handleRemoveMount}
style={removeStyle}>
<RootElem
col={(dimensions) => dimensions.columns - 6}
row={secondRow + 3}
>
<a href={'#'} onClick={handleRemoveMount} style={removeStyle}>
<FontAwesomeIcon icon={faTrashAlt} />
</a>
</RootElem>);
</RootElem>
);
}
const isSkynet = (props.provider === 'Skynet');
const isSkynet = props.provider === 'Skynet';
return (
<div className={'MountItem'}>
<Grid noScroll>
@@ -185,28 +240,49 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
col={configButton ? 6 : 0}
rowSpan={5}
colSpan={90}
text={props.remote ? props.provider.substr(6) : props.s3 ? props.provider.substr(2) : isSkynet ? props.provider + ' [EXPERIMENTAL]' : props.provider}
text={
props.remote
? props.provider.substr(6)
: props.s3
? props.provider.substr(2)
: isSkynet
? props.provider + ' [EXPERIMENTAL]'
: props.provider
}
textAlign={'Left'}
type={'Heading2'}/>
{(isSkynet && (props.MState.Mounted)) ? (
<a href={'#'}
type={'Heading2'}
/>
{isSkynet && props.MState.Mounted ? (
<a
href={'#'}
col={(configButton ? 24 : 18) + 34}
onClick={props.MState.AllowMount ? () => props.displaySkynetExport(true) : e => {
onClick={
props.MState.AllowMount
? () => props.displaySkynetExport(true)
: (e) => {
e.preventDefault();
}}
}
}
rowSpan={5}
style={{...pointer, fontWeight: 'normal'}}>
style={{ ...pointer, fontWeight: 'normal' }}
>
<u>Export</u>
</a>
) : null}
{(isSkynet && (props.MState.Mounted)) ? (
<a href={'#'}
{isSkynet && props.MState.Mounted ? (
<a
href={'#'}
col={(configButton ? 24 + 13 : 18 + 13) + 34}
onClick={props.MState.AllowMount ? () => props.displaySkynetImport(true) : e => {
onClick={
props.MState.AllowMount
? () => props.displaySkynetImport(true)
: (e) => {
e.preventDefault();
}}
}
}
rowSpan={5}
style={{...pointer, fontWeight: 'normal'}}>
style={{ ...pointer, fontWeight: 'normal' }}
>
<u>Import</u>
</a>
) : null}

View File

@@ -14,7 +14,7 @@ import {
setBusy,
setMounted,
setMountState,
setProviderState
setProviderState,
} from '../../redux/actions/mount_actions';
import { notifyError } from '../../redux/actions/error_actions';
@@ -29,7 +29,7 @@ class MountItems extends IPCContainer {
RetryItems: {},
};
addMountsBusy = provider => {
addMountsBusy = (provider) => {
this.props.setMountsBusy(true);
this.activeDetections.push(provider);
};
@@ -43,22 +43,34 @@ class MountItems extends IPCContainer {
...this.state.RetryItems,
};
delete retryItems[provider];
this.setState({
this.setState(
{
DisplayRetry: Object.keys(retryItems).length > 0,
RetryItems: retryItems,
}, () => {
},
() => {
if (this.state.DisplayRetry) {
this.sendRequest(Constants.IPC_Show_Window);
}
stateCallback();
});
}
);
}
};
componentDidMount() {
this.setRequestHandler(Constants.IPC_Detect_Mount_Reply, this.onDetectMountReply);
this.setRequestHandler(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
this.setRequestHandler(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
this.setRequestHandler(
Constants.IPC_Detect_Mount_Reply,
this.onDetectMountReply
);
this.setRequestHandler(
Constants.IPC_Mount_Drive_Reply,
this.onMountDriveReply
);
this.setRequestHandler(
Constants.IPC_Unmount_Drive_Reply,
this.onUnmountDriveReply
);
this.props.resetMountsState();
this.detectMounts();
}
@@ -73,9 +85,9 @@ class MountItems extends IPCContainer {
this.props.resetMountsState();
this.activeDetections = [];
super.componentWillUnmount();
};
}
detectMount = provider => {
detectMount = (provider) => {
this.addMountsBusy(provider);
this.sendRequest(Constants.IPC_Detect_Mount, {
@@ -89,7 +101,7 @@ class MountItems extends IPCContainer {
detectMounts = () => {
if (!this.state.DisplayRetry) {
const providerList = this.getProviderList();
providerList.forEach(provider => {
providerList.forEach((provider) => {
this.detectMount(provider);
});
}
@@ -98,7 +110,7 @@ class MountItems extends IPCContainer {
displayRetryMount = (provider, remote, s3, mountLocation, msg) => {
if (!this.state.RetryItems[provider]) {
let retryItems = {
...this.state.RetryItems
...this.state.RetryItems,
};
retryItems[provider] = {
RetrySeconds: 10,
@@ -110,10 +122,12 @@ class MountItems extends IPCContainer {
};
this.props.setMountState(provider, mountState);
this.setState({
this.setState(
{
DisplayRetry: true,
RetryItems: retryItems,
}, () => {
},
() => {
this.sendRequest(Constants.IPC_Show_Window);
this.retryIntervals[provider] = setInterval(() => {
let retryItems = {
@@ -122,7 +136,13 @@ class MountItems extends IPCContainer {
const retrySeconds = retryItems[provider].RetrySeconds - 1;
if (retrySeconds === 0) {
this.cancelRetryMount(provider, () => {
this.handleMountUnMount(provider, remote, s3, true, mountLocation);
this.handleMountUnMount(
provider,
remote,
s3,
true,
mountLocation
);
});
} else {
retryItems[provider].RetrySeconds = retrySeconds;
@@ -131,7 +151,8 @@ class MountItems extends IPCContainer {
});
}
}, 1000);
});
}
);
}
};
@@ -140,7 +161,7 @@ class MountItems extends IPCContainer {
Title: provider + ' Mount Location',
Location: location,
});
if (location && (location.length > 0)) {
if (location && location.length > 0) {
this.handleMountLocationChanged(provider, location);
}
};
@@ -154,25 +175,29 @@ class MountItems extends IPCContainer {
};
handleMountUnMount = (provider, remote, s3, mount, location) => {
if (!location || (location.trim().length === 0)) {
if (!location || location.trim().length === 0) {
this.props.notifyError('Mount location is not set');
} else {
let allowAction = true;
if (mount) {
let result = remote || s3 || provider === 'Skynet' ?
{Valid: true, Success: true} :
this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
let result =
remote || s3 || provider === 'Skynet'
? { Valid: true, Success: true }
: this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
Provider: provider,
Remote: remote,
S3: s3,
Version: this.props.InstalledVersion
Version: this.props.InstalledVersion,
}).data;
if (result.Success) {
if (result.Valid) {
if (this.props.Platform !== 'win32') {
result = this.sendSyncRequest(Constants.IPC_Check_Mount_Location, {
result = this.sendSyncRequest(
Constants.IPC_Check_Mount_Location,
{
Location: location,
});
}
);
if (!result.Success) {
allowAction = false;
this.props.notifyError(result.Error.toString());
@@ -180,20 +205,56 @@ class MountItems extends IPCContainer {
}
} else {
allowAction = false;
if ((result.Code === new Uint32Array([-1])[0]) || (result.Code === new Uint8Array([-1])[0])) {
this.displayRetryMount(provider, remote, s3, location, 'Failed to connect to ' + provider + ' daemon');
} else if ((result.Code === new Uint32Array([-3])[0]) || (result.Code === new Uint8Array([-3])[0])) {
this.displayRetryMount(provider, remote, s3, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.');
if (
result.Code === new Uint32Array([-1])[0] ||
result.Code === new Uint8Array([-1])[0]
) {
this.displayRetryMount(
provider,
remote,
s3,
location,
'Failed to connect to ' + provider + ' daemon'
);
} else if (
result.Code === new Uint32Array([-3])[0] ||
result.Code === new Uint8Array([-3])[0]
) {
this.displayRetryMount(
provider,
remote,
s3,
location,
'Incompatible ' +
provider +
' daemon. Please upgrade ' +
provider +
'.'
);
} else {
this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error);
this.displayRetryMount(
provider,
remote,
s3,
location,
'Version check failed: ' + result.Error
);
}
}
} else {
allowAction = false;
if (this.props.Platform === 'win32') {
this.props.notifyError('Failed to launch repertory. Please install Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019.');
this.props.notifyError(
'Failed to launch repertory. Please install Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019.'
);
} else {
this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error);
this.displayRetryMount(
provider,
remote,
s3,
location,
'Version check failed: ' + result.Error
);
}
}
}
@@ -223,11 +284,13 @@ class MountItems extends IPCContainer {
}
};
getProviderList = providersOnly => {
const providerList = Constants.PROVIDER_LIST.filter(i => {
return ((i === 'Skynet') && this.props.skynetSupported) ||
((i === 'ScPrime') && this.props.scPrimeSupported) ||
((i === 'Sia') && this.props.siaSupported);
getProviderList = (providersOnly) => {
const providerList = Constants.PROVIDER_LIST.filter((i) => {
return (
(i === 'Skynet' && this.props.skynetSupported) ||
(i === 'ScPrime' && this.props.scPrimeSupported) ||
(i === 'Sia' && this.props.siaSupported)
);
});
let remoteList = [];
@@ -240,17 +303,12 @@ class MountItems extends IPCContainer {
s3List = [...this.props.S3Mounts];
}
return [
...providerList,
...remoteList,
...s3List,
];
return [...providerList, ...remoteList, ...s3List];
};
hasActiveMount = () => {
for (const provider of Object.keys(this.props.MountState)) {
if (this.props.MountState[provider].Mounted)
return true;
if (this.props.MountState[provider].Mounted) return true;
}
return false;
@@ -259,7 +317,11 @@ class MountItems extends IPCContainer {
onDetectMountReply = (event, arg) => {
const provider = arg.data.Provider;
if (!this.state.RetryItems[provider]) {
if (arg.data.Success && (!arg.data.Active || (arg.data.Location && (arg.data.Location.length > 0)))) {
if (
arg.data.Success &&
(!arg.data.Active ||
(arg.data.Location && arg.data.Location.length > 0))
) {
const mountState = {
AllowMount: true,
DriveLetters: arg.data.DriveLetters,
@@ -267,7 +329,12 @@ class MountItems extends IPCContainer {
};
this.props.setMountState(provider, mountState);
this.updateMountLocation(provider, arg.data.Location, mountState.Mounted, arg.data.DriveLetters);
this.updateMountLocation(
provider,
arg.data.Location,
mountState.Mounted,
arg.data.DriveLetters
);
this.props.setAutoMountProcessed(provider, true);
this.removeMountsBusy(provider);
} else {
@@ -284,39 +351,63 @@ class MountItems extends IPCContainer {
};
onUnmountDriveReply = (event, arg) => {
if (arg && arg.data && !arg.data.Expected && arg.data.Location && this.props.ProviderState[arg.data.Provider].AutoRestart) {
this.displayRetryMount(arg.data.Provider, arg.data.Remote, arg.data.Location);
if (
arg &&
arg.data &&
!arg.data.Expected &&
arg.data.Location &&
this.props.ProviderState[arg.data.Provider].AutoRestart
) {
this.displayRetryMount(
arg.data.Provider,
arg.data.Remote,
arg.data.Location
);
} else {
this.detectMount(arg.data.Provider);
}
this.removeMountsBusy(arg.data.Provider);
};
removeMountsBusy = provider => {
removeMountsBusy = (provider) => {
const idx = this.activeDetections.indexOf(provider);
if (idx > -1) {
this.activeDetections.splice(idx, 1);
}
this.props.setMountsBusy((this.activeDetections.length > 0) || this.hasActiveMount());
this.props.setMountsBusy(
this.activeDetections.length > 0 || this.hasActiveMount()
);
};
updateMountLocation = (provider, location, mounted, driveLetters) => {
const providerState = this.props.ProviderState[provider];
if (location.length === 0) {
location = (this.props.Platform === 'win32') ?
!providerState.MountLocation || providerState.MountLocation.trim().length === 0 ? driveLetters[0] : providerState.MountLocation :
providerState.MountLocation;
location =
this.props.Platform === 'win32'
? !providerState.MountLocation ||
providerState.MountLocation.trim().length === 0
? driveLetters[0]
: providerState.MountLocation
: providerState.MountLocation;
}
if (location !== providerState.MountLocation) {
this.handleMountLocationChanged(provider, location);
}
if (!this.props.AutoMountProcessed[provider] &&
if (
!this.props.AutoMountProcessed[provider] &&
this.props.ProviderState[provider].AutoMount &&
!mounted &&
(location.length > 0)) {
this.handleMountUnMount(provider, this.props.RemoteMounts.includes(provider), this.props.S3Mounts.includes(provider), true, location);
location.length > 0
) {
this.handleMountUnMount(
provider,
this.props.RemoteMounts.includes(provider),
this.props.S3Mounts.includes(provider),
true,
location
);
}
};
@@ -328,85 +419,137 @@ class MountItems extends IPCContainer {
for (const provider in this.state.RetryItems) {
if (this.state.RetryItems.hasOwnProperty(provider)) {
if (this.state.RetryItems[provider].RetryMessage) {
retryList.push(<p key={'rl_' + retryList.length}>{this.state.RetryItems[provider].RetryMessage}</p>);
retryList.push(
<p key={'rl_' + retryList.length}>
{this.state.RetryItems[provider].RetryMessage}
</p>
);
}
retryList.push(<Button clicked={() => this.cancelRetryMount(provider, () => this.detectMounts())}
key={'rl_' + retryList.length}>Cancel {provider} Remount
({this.state.RetryItems[provider].RetrySeconds}s)</Button>);
retryList.push(
<Button
clicked={() =>
this.cancelRetryMount(provider, () => this.detectMounts())
}
key={'rl_' + retryList.length}
>
Cancel {provider} Remount (
{this.state.RetryItems[provider].RetrySeconds}s)
</Button>
);
if (++retryCount < Object.keys(this.state.RetryItems).length) {
retryList.push(<div style={{paddingTop: 'var(--default_spacing)'}}
key={'rl_' + retryList.length}/>);
retryList.push(
<div
style={{ paddingTop: 'var(--default_spacing)' }}
key={'rl_' + retryList.length}
/>
);
}
}
}
retryDisplay = (
<Modal>
<Box dxDark dxStyle={{padding: 'var(--default_spacing)', minWidth: '70vw'}}>
<h1 style={{
<Box
dxDark
dxStyle={{ padding: 'var(--default_spacing)', minWidth: '70vw' }}
>
<h1
style={{
textAlign: 'center',
paddingBottom: 'var(--default_spacing)',
color: 'var(--text_color_error)'
}}>Mount Failed</h1>
color: 'var(--text_color_error)',
}}
>
Mount Failed
</h1>
{retryList}
</Box>
</Modal>
)
);
}
let footerItems = [];
if (this.props.remoteSupported || this.props.s3Supported) {
footerItems.push(<AddMount remoteSupported={this.props.remoteSupported}
footerItems.push(
<AddMount
remoteSupported={this.props.remoteSupported}
s3Supported={this.props.s3Supported}
key={'hi_' + footerItems.length}/>);
key={'hi_' + footerItems.length}
/>
);
} else {
footerItems.push(<div key={'hi_' + footerItems.length}
style={{height: '27px'}}/>);
footerItems.push(
<div key={'hi_' + footerItems.length} style={{ height: '27px' }} />
);
}
let items = [];
for (const provider of this.getProviderList(true)) {
items.push((
<MountItem allowRemove={false}
items.push(
<MountItem
allowRemove={false}
browseClicked={this.handleBrowseLocation}
changed={e => this.handleMountLocationChanged(provider, e.target.value)}
changed={(e) =>
this.handleMountLocationChanged(provider, e.target.value)
}
clicked={this.handleMountUnMount}
key={'it_' + items.length}
provider={provider}/>
));
items.push(<div key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}} />)
provider={provider}
/>
);
items.push(
<div
key={'it_' + items.length}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
}
if (this.props.remoteSupported) {
for (const provider of this.props.RemoteMounts) {
items.push((
<MountItem allowRemove={true}
items.push(
<MountItem
allowRemove={true}
browseClicked={this.handleBrowseLocation}
changed={e => this.handleMountLocationChanged(provider, e.target.value)}
changed={(e) =>
this.handleMountLocationChanged(provider, e.target.value)
}
clicked={this.handleMountUnMount}
key={'it_' + items.length}
provider={provider}
remote/>
));
items.push(<div key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}}/>)
remote
/>
);
items.push(
<div
key={'it_' + items.length}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
}
}
if (this.props.s3Supported) {
for (const provider of this.props.S3Mounts) {
items.push((
<MountItem allowRemove={true}
items.push(
<MountItem
allowRemove={true}
browseClicked={this.handleBrowseLocation}
changed={e => this.handleMountLocationChanged(provider, e.target.value)}
changed={(e) =>
this.handleMountLocationChanged(provider, e.target.value)
}
clicked={this.handleMountUnMount}
key={'it_' + items.length}
provider={provider}
s3/>
));
items.push(<div key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}}/>)
s3
/>
);
items.push(
<div
key={'it_' + items.length}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
}
}
@@ -416,16 +559,22 @@ class MountItems extends IPCContainer {
<div style={{ margin: 0, padding: 0 }}>
{retryDisplay}
<div
className={this.props.remoteSupported || this.props.s3Supported ? 'MountItemsRemote' : 'MountItems'}>
className={
this.props.remoteSupported || this.props.s3Supported
? 'MountItemsRemote'
: 'MountItems'
}
>
{items}
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
{footerItems}
</div>);
</div>
);
}
}
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AutoMountProcessed: state.mounts.AutoMountProcessed,
InstalledVersion: state.relver.InstalledVersion,
@@ -435,20 +584,25 @@ const mapStateToProps = state => {
ProviderState: state.mounts.ProviderState,
RemoteMounts: state.mounts.RemoteMounts,
S3Mounts: state.mounts.S3Mounts,
}
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
notifyError: (msg, critical, callback) =>
dispatch(notifyError(msg, critical, callback)),
resetMountsState: () => dispatch(resetMountsState()),
setAllowMount: (provider, allow) => dispatch(setAllowMount(provider, allow)),
setAutoMountProcessed: (provider, processed) => dispatch(setAutoMountProcessed(provider, processed)),
setAllowMount: (provider, allow) =>
dispatch(setAllowMount(provider, allow)),
setAutoMountProcessed: (provider, processed) =>
dispatch(setAutoMountProcessed(provider, processed)),
setMounted: (provider, mounted) => dispatch(setMounted(provider, mounted)),
setMountsBusy: busy => dispatch(setBusy(busy)),
setMountState: (provider, state) => dispatch(setMountState(provider, state)),
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
}
setMountsBusy: (busy) => dispatch(setBusy(busy)),
setMountState: (provider, state) =>
dispatch(setMountState(provider, state)),
setProviderState: (provider, state) =>
dispatch(setProviderState(provider, state)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MountItems);

View File

@@ -13,32 +13,40 @@ import CheckBox from '../../components/UI/CheckBox/CheckBox';
const Constants = require('../../constants');
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
DisplayS3Configuration: state.mounts.DisplayS3Configuration,
}
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
displayPinnedManager: display => dispatch(displayPinnedManager(display)),
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
displayPinnedManager: (display) => dispatch(displayPinnedManager(display)),
notifyApplicationBusy: (busy) =>
dispatch(notifyApplicationBusy(busy, true)),
notifyError: (msg, cb) => dispatch(notifyError(msg, false, cb)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer {
export default connect(
mapStateToProps,
mapDispatchToProps
)(
class extends IPCContainer {
state = {
active_directory: '/',
items: [],
previous: [],
}
};
componentDidMount() {
this.setRequestHandler(Constants.IPC_Get_Directory_Items_Reply, this.onGetDirectoryItemsReply);
this.setRequestHandler(
Constants.IPC_Get_Directory_Items_Reply,
this.onGetDirectoryItemsReply
);
this.grabDirectoryItems();
}
@@ -54,21 +62,26 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
Version: this.props.version,
Path: this.state.active_directory,
});
}
};
onGetDirectoryItemsReply = (_, { data }) => {
if (data.Success) {
const items = data.Items
.filter(i => i.path !== '.' && (this.state.active_directory !== '/' || (i.path.substr(0, 1) !== '.')))
.map(i => {
const items = data.Items.filter(
(i) =>
i.path !== '.' &&
(this.state.active_directory !== '/' || i.path.substr(0, 1) !== '.')
).map((i) => {
return {
...i,
name: i.path === '..' ? i.path : i.path.substr(i.path.lastIndexOf('/') + 1),
name:
i.path === '..'
? i.path
: i.path.substr(i.path.lastIndexOf('/') + 1),
meta: {
...i.meta,
pinned: i.meta.pinned === '1',
}
}
},
};
});
this.setState({
items,
@@ -78,16 +91,17 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
this.props.displayPinnedManager(false);
});
}
}
};
createDirectory = (name, path, idx, total, item_idx) => {
const style = {}
const style = {};
if (item_idx + 1 !== total) {
style.marginBottom = '4px';
}
return (
<div key={'dir_' + idx} style={{ ...style }}>
<Button buttonStyles={{textAlign: 'left'}}
<Button
buttonStyles={{ textAlign: 'left' }}
clicked={() => {
const previous = [...this.state.previous];
if (path === '..') {
@@ -95,38 +109,48 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
} else {
previous.push(this.state.active_directory);
}
this.setState({
this.setState(
{
items: [],
active_directory: path,
previous,
}, () => {
},
() => {
this.grabDirectoryItems();
});
}}>
<FontAwesomeIcon icon={faFolder}
}
);
}}
>
<FontAwesomeIcon
icon={faFolder}
fixedWidth
color={'var(--heading_text_color)'}
style={{padding: 0, margin: 0}}/>
style={{ padding: 0, margin: 0 }}
/>
&nbsp;{name}
</Button>
</div>
);
}
};
createFile = (name, path, pinned, idx, total, item_idx) => {
const style = {textAlign: 'left'}
const style = { textAlign: 'left' };
if (item_idx + 1 !== total) {
style.marginBottom = '2px';
}
return (
<div key={'file_' + idx} style={{ ...style }}>
<CheckBox checked={pinned}
<CheckBox
checked={pinned}
changed={() => {
const items = JSON.parse(JSON.stringify(this.state.items));
const pinned = items[item_idx].meta.pinned = !items[item_idx].meta.pinned;
this.setState({
items
}, () => {
const pinned = (items[item_idx].meta.pinned = !items[item_idx]
.meta.pinned);
this.setState(
{
items,
},
() => {
this.sendSyncRequest(Constants.IPC_Set_Pinned, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
@@ -135,46 +159,69 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
Path: path,
Pinned: pinned,
});
});
}
);
}}
label={name}/>
label={name}
/>
</div>
);
}
};
render() {
let idx = 0;
return (
<Box dxDark dxStyle={{
<Box
dxDark
dxStyle={{
height: 'calc(100vh - (var(--default_spacing) * 4)',
padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)'
}}>
width: 'calc(100vw - (var(--default_spacing) * 4)',
}}
>
<div className={'PinnedManager'}>
<div className={'PinnedManagerHeading'}>
<div className={'PinnedManagerClose'}>
<a href={'#'}
<a
href={'#'}
onClick={() => this.props.displayPinnedManager(false)}
style={{cursor: 'pointer', flex: '0'}}>X</a>
style={{ cursor: 'pointer', flex: '0' }}
>
X
</a>
</div>
<h1 style={{width: '100%', textAlign: 'center'}}>{'Pinned File Manager'}</h1>
<h1 style={{ width: '100%', textAlign: 'center' }}>
{'Pinned File Manager'}
</h1>
<div className={'PinnedManagerActiveDirectory'}>
<b>&nbsp;{this.state.active_directory}</b>
</div>
</div>
<div className={'PinnedManagerItemsOwner'}>
<div className={'PinnedManagerItems'}>
{
this.state.items.map((i, k) => {
return i.directory ?
this.createDirectory(i.name, i.path, idx++, this.state.items.length, k) :
this.createFile(i.name, i.path, i.meta.pinned, idx++, this.state.items.length, k);
})
}
{this.state.items.map((i, k) => {
return i.directory
? this.createDirectory(
i.name,
i.path,
idx++,
this.state.items.length,
k
)
: this.createFile(
i.name,
i.path,
i.meta.pinned,
idx++,
this.state.items.length,
k
);
})}
</div>
</div>
</div>
</Box>
)
);
}
});
}
);

View File

@@ -7,7 +7,7 @@ import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import {
downloadItem,
setAllowDownload
setAllowDownload,
} from '../../redux/actions/download_actions';
import DropDown from '../../components/UI/DropDown/DropDown';
import IPCContainer from '../IPCContainer/IPCContainer';
@@ -19,25 +19,32 @@ class SelectAppPlatform extends IPCContainer {
Selected: 0,
};
grabLatestRelease = appPlatform => {
const errorHandler = error => {
grabLatestRelease = (appPlatform) => {
const errorHandler = (error) => {
this.props.notifyError(error);
this.props.setInstallTestActive(false);
this.props.setAllowDownload(false);
};
axios
.get(Constants.RELEASES_URL)
.then(response => {
.then((response) => {
try {
const releases = response.data.Versions.Release[appPlatform];
const latestVersion = releases[releases.length - 1];
const release = response.data.Locations[appPlatform][latestVersion];
this.props.downloadItem(latestVersion + '.zip', Constants.INSTALL_TYPES.TestRelease, release.urls, false, latestVersion, appPlatform);
this.props.downloadItem(
latestVersion + '.zip',
Constants.INSTALL_TYPES.TestRelease,
release.urls,
false,
latestVersion,
appPlatform
);
} catch (error) {
errorHandler(error);
}
})
.catch(error => {
.catch((error) => {
errorHandler(error);
});
};
@@ -45,10 +52,12 @@ class SelectAppPlatform extends IPCContainer {
handleTestClicked = () => {
this.props.setInstallTestActive(true);
this.props.setAllowDownload(true);
this.grabLatestRelease(Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected])
this.grabLatestRelease(
Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]
);
};
handleChanged = e => {
handleChanged = (e) => {
this.setState({
...this.state,
Selected: Constants.LINUX_SELECTABLE_PLATFORMS.indexOf(e.target.value),
@@ -60,33 +69,45 @@ class SelectAppPlatform extends IPCContainer {
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'SAPHeading'}>Select Linux Platform</h1>
<div className={'SAPContent'}>
<p>Repertory was unable to detect your Linux distribution. Please select one of the following and click <b>Test</b> to continue:</p>
<p>
Repertory was unable to detect your Linux distribution. Please
select one of the following and click <b>Test</b> to continue:
</p>
</div>
<div className={'SAPActions'}>
<DropDown changed={this.handleChanged}
<DropDown
changed={this.handleChanged}
disabled={this.props.InstallTestActive}
items={Constants.LINUX_SELECTABLE_PLATFORMS}
selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}/>
<Button clicked={this.handleTestClicked}
disabled={this.props.InstallTestActive}>Test</Button>
selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}
/>
<Button
clicked={this.handleTestClicked}
disabled={this.props.InstallTestActive}
>
Test
</Button>
</div>
</Box>
);
}
}
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
InstallTestActive: state.install.InstallTestActive,
}
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
downloadItem: (name, type, urls, isWinFSP, testVersion, appPlatform) => dispatch(downloadItem(name, type, urls, isWinFSP, testVersion, appPlatform)),
notifyError: msg => dispatch(notifyError(msg)),
setAllowDownload: allow => dispatch(setAllowDownload(allow)),
setInstallTestActive: active => dispatch(setInstallTestActive(active)),
downloadItem: (name, type, urls, isWinFSP, testVersion, appPlatform) =>
dispatch(
downloadItem(name, type, urls, isWinFSP, testVersion, appPlatform)
),
notifyError: (msg) => dispatch(notifyError(msg)),
setAllowDownload: (allow) => dispatch(setAllowDownload(allow)),
setInstallTestActive: (active) => dispatch(setInstallTestActive(active)),
};
};

View File

@@ -9,50 +9,69 @@ 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
faCheckSquare,
faChevronDown,
faChevronRight,
faFile,
faFolder,
faFolderOpen,
faHSquare,
faMinusSquare,
faPlusSquare,
faSquare,
} from '@fortawesome/free-solid-svg-icons';
import Button from '../../components/UI/Button/Button';
const Constants = require('../../constants');
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AppBusy: state.common.AppBusy,
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
displaySkynetExport: display => dispatch(displaySkynetExport(display)),
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
notifyError: msg => dispatch(notifyError(msg)),
displaySkynetExport: (display) => dispatch(displaySkynetExport(display)),
notifyApplicationBusy: (busy) =>
dispatch(notifyApplicationBusy(busy, true)),
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer {
export default connect(
mapStateToProps,
mapDispatchToProps
)(
class extends IPCContainer {
state = {
checked: [],
clicked: {},
expanded: [],
nodes: [],
second_stage: false,
}
};
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});
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 => {
createNodes = (items) => {
/*
{
name: '',
@@ -66,7 +85,12 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
const treeItem = {
label: item.name,
path: item.path,
value: item.name === '/' ? 0 : item.path ? item.path : JSON.stringify(item),
value:
item.name === '/'
? 0
: item.path
? item.path
: JSON.stringify(item),
};
if (item.directory) {
@@ -77,7 +101,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
}
return ret;
}
};
handleNavigation = () => {
if (this.state.second_stage) {
@@ -95,25 +119,34 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
});
}
}
}
};
onExportSkylinksReply = (_, arg) => {
this.props.notifyApplicationBusy(false);
if (arg.data.Success) {
this.setState({
this.setState(
{
checked: [],
clicked: {},
expanded: [],
nodes: [],
second_stage: false,
}, () => {
this.props.notifyInfo('Skylink Exports', '!alternate!!copyable!' + JSON.stringify(arg.data.Result.success, null, 2));
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version});
},
() => {
this.props.notifyInfo(
'Skylink Exports',
'!alternate!!copyable!' +
JSON.stringify(arg.data.Result.success, null, 2)
);
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {
Version: this.props.version,
});
}
);
} else {
this.props.notifyError(arg.data.Error);
}
}
};
onGrabSkynetTreeReply = (_, arg) => {
if (arg.data.Success) {
@@ -123,23 +156,31 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
nodes: this.createNodes(arg.data.Result),
});
} else {
this.setState({
this.setState(
{
checked: [],
expanded: [],
nodes: [],
}, () => {
},
() => {
this.props.notifyError(arg.data.Error);
});
}
);
}
};
render() {
return this.props.AppBusy ? (<div/>) : (
<Box dxDark dxStyle={{
return this.props.AppBusy ? (
<div />
) : (
<Box
dxDark
dxStyle={{
height: '90vh',
padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)'
}}>
width: 'calc(100vw - (var(--default_spacing) * 4)',
}}
>
<div
style={{
float: 'right',
@@ -147,89 +188,161 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
padding: 0,
marginTop: '-4px',
boxSizing: 'border-box',
display: 'block'
}}>
<a href={'#'}
display: 'block',
}}
>
<a
href={'#'}
onClick={() => this.props.displaySkynetExport(false)}
style={{cursor: 'pointer'}}>X</a>
style={{ cursor: 'pointer' }}
>
X
</a>
</div>
<h1
className={'SkynetExportHeading'}>{this.state.second_stage ? 'Verify Exports' : 'Export Files'}</h1>
<div className={this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'}>
{
this.state.second_stage ?
this.state.checked.map(path => {
<h1 className={'SkynetExportHeading'}>
{this.state.second_stage ? 'Verify Exports' : 'Export Files'}
</h1>
<div
className={
this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'
}
>
{this.state.second_stage ? (
this.state.checked.map((path) => {
return (
<input readOnly
<input
readOnly
className={'ConfigurationItemInput'}
key={path}
style={{width: '100%', marginBottom: 'var(--default_spacing)'}}
style={{
width: '100%',
marginBottom: 'var(--default_spacing)',
}}
type={'text'}
value={path}/>
value={path}
/>
);
})
: (
<CheckboxTree checked={this.state.checked}
) : (
<CheckboxTree
checked={this.state.checked}
expanded={this.state.expanded}
expandOnClick
showExpandAll
icons={{
check: <FontAwesomeIcon icon={faCheckSquare} fixedWidth
style={{padding: 0, margin: 0}}/>,
uncheck: <FontAwesomeIcon icon={faSquare} fixedWidth
style={{padding: 0, margin: 0}}/>,
halfCheck: <FontAwesomeIcon icon={faHSquare} fixedWidth
style={{padding: 0, margin: 0}}/>,
expandClose: <FontAwesomeIcon icon={faChevronRight} fixedWidth
style={{padding: 0, margin: 0}}/>,
expandOpen: <FontAwesomeIcon icon={faChevronDown} fixedWidth
style={{padding: 0, margin: 0}}/>,
expandAll: <FontAwesomeIcon icon={faPlusSquare} fixedWidth
style={{padding: 0, margin: 0}}/>,
collapseAll: <FontAwesomeIcon icon={faMinusSquare} fixedWidth
style={{padding: 0, margin: 0}}/>,
parentClose: <FontAwesomeIcon icon={faFolder}
check: (
<FontAwesomeIcon
icon={faCheckSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
uncheck: (
<FontAwesomeIcon
icon={faSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
halfCheck: (
<FontAwesomeIcon
icon={faHSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
expandClose: (
<FontAwesomeIcon
icon={faChevronRight}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
expandOpen: (
<FontAwesomeIcon
icon={faChevronDown}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
expandAll: (
<FontAwesomeIcon
icon={faPlusSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
collapseAll: (
<FontAwesomeIcon
icon={faMinusSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
parentClose: (
<FontAwesomeIcon
icon={faFolder}
fixedWidth
color={'var(--heading_text_color)'}
style={{padding: 0, margin: 0}}/>,
parentOpen: <FontAwesomeIcon icon={faFolderOpen}
style={{ padding: 0, margin: 0 }}
/>
),
parentOpen: (
<FontAwesomeIcon
icon={faFolderOpen}
fixedWidth
color={'var(--heading_text_color)'}
style={{padding: 0, margin: 0}}/>,
leaf: <FontAwesomeIcon icon={faFile}
style={{ padding: 0, margin: 0 }}
/>
),
leaf: (
<FontAwesomeIcon
icon={faFile}
fixedWidth
color={'var(--text_color)'}
style={{padding: 0, margin: 0}}/>
style={{ padding: 0, margin: 0 }}
/>
),
}}
nodes={this.state.nodes}
onClick={clicked => this.setState({clicked})}
onCheck={checked => this.setState({checked})}
onExpand={expanded => this.setState({expanded})}/>
)
}
onClick={(clicked) => this.setState({ clicked })}
onCheck={(checked) => this.setState({ checked })}
onExpand={(expanded) => this.setState({ expanded })}
/>
)}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
{
this.state.second_stage ?
<Button buttonStyles={{
{this.state.second_stage ? (
<Button
buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto'
}} clicked={() => this.setState({
second_stage: false,
})}>{'Back'}</Button> :
null
}
<Button buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto'
width: 'auto',
}}
clicked={this.handleNavigation}>{this.state.second_stage ? 'Export' : 'Next'}</Button>
clicked={() =>
this.setState({
second_stage: false,
})
}
>
{'Back'}
</Button>
) : null}
<Button
buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto',
}}
clicked={this.handleNavigation}
>
{this.state.second_stage ? 'Export' : 'Next'}
</Button>
</div>
</Box>
);
}
});
}
);

View File

@@ -1,30 +1,36 @@
import React from 'react'
import './Import.css'
import React from 'react';
import './Import.css';
const Import = ({ data }) => {
return (
<div className={'ImportOwner'}>
<input readOnly
<input
readOnly
className={'ConfigurationItemInput'}
style={{
maxWidth: 'calc(33.33% - var(--default_spacing)',
marginRight: 'var(--default_spacing)'
marginRight: 'var(--default_spacing)',
}}
type={'text'}
value={data.directory}/>
<input readOnly
value={data.directory}
/>
<input
readOnly
className={'ConfigurationItemInput'}
style={{ maxWidth: 'calc(33.33% - calc(var(--default_spacing) / 2))' }}
type={'text'}
value={data.skylink}/>
<input readOnly
value={data.skylink}
/>
<input
readOnly
className={'ConfigurationItemInput'}
style={{
maxWidth: 'calc(33.33% - calc(var(--default_spacing) / 2))',
marginLeft: 'var(--default_spacing)'
marginLeft: 'var(--default_spacing)',
}}
type={'text'}
value={data.token}/>
value={data.token}
/>
</div>
);
};

View File

@@ -1,6 +1,6 @@
import React from 'react'
import './ImportList.css'
import Import from './Import/Import'
import React from 'react';
import './ImportList.css';
import Import from './Import/Import';
import Text from '../../../components/UI/Text/Text';
const ImportList = ({ imports_array }) => {
@@ -8,22 +8,31 @@ const ImportList = ({imports_array}) => {
return (
<div>
<div className={'ImportListHeader'}>
<Text type={'Heading1'} text={'Directory'}
style={{minWidth: '33.33%', maxWidth: '33.33%'}}/>
<Text type={'Heading1'} text={'Skylink'} style={{minWidth: '33.33%', maxWidth: '33.33%'}}/>
<Text type={'Heading1'} text={'Token'} style={{minWidth: '33.33%', maxWidth: '33.33%'}}/>
<Text
type={'Heading1'}
text={'Directory'}
style={{ minWidth: '33.33%', maxWidth: '33.33%' }}
/>
<Text
type={'Heading1'}
text={'Skylink'}
style={{ minWidth: '33.33%', maxWidth: '33.33%' }}
/>
<Text
type={'Heading1'}
text={'Token'}
style={{ minWidth: '33.33%', maxWidth: '33.33%' }}
/>
</div>
<hr />
<div className={'ImportListOwner'}>
{
imports_array.map(data => {
{imports_array.map((data) => {
return (
<div key={'import_' + key++}>
<Import data={data} />
</div>
);
})
}
})}
</div>
</div>
);

View File

@@ -1,36 +1,37 @@
import React from 'react'
import React from 'react';
import { connect } from 'react-redux';
import './SkynetImport.css'
import './SkynetImport.css';
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import { displaySkynetImport } from '../../redux/actions/skynet_actions';
import ImportList from './ImportList/ImportList'
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';
import { notifyError, notifyInfo } from '../../redux/actions/error_actions';
const Constants = require('../../constants');
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
AppBusy: state.common.AppBusy,
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch) => {
return {
displaySkynetImport: display => dispatch(displaySkynetImport(display)),
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
notifyError: msg => dispatch(notifyError(msg)),
displaySkynetImport: (display) => dispatch(displaySkynetImport(display)),
notifyApplicationBusy: (busy) =>
dispatch(notifyApplicationBusy(busy, true)),
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport extends IPCContainer {
export default connect(
mapStateToProps,
mapDispatchToProps
)(
class SkynetImport extends IPCContainer {
state = {
import_text: '',
imports_array: [],
@@ -38,7 +39,10 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
};
componentDidMount() {
this.setRequestHandler(Constants.IPC_Import_Skylinks_Reply, this.onImportSkylinksReply);
this.setRequestHandler(
Constants.IPC_Import_Skylinks_Reply,
this.onImportSkylinksReply
);
}
componentWillUnmount() {
@@ -46,7 +50,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
}
displaySyntax = () => {
const msg = '!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' +
const msg =
'!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' +
' AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA\n' +
' AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg\n' +
'\n' +
@@ -71,8 +76,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
' "token": "My Password"\n' +
' }\n' +
' ]';
this.props.notifyInfo('Import Syntax', msg)
}
this.props.notifyInfo('Import Syntax', msg);
};
handleNavigation = () => {
if (this.state.second_stage) {
@@ -96,7 +101,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
importsArray = JSON.parse(this.state.import_text.trim());
break;
} else if (item.indexOf('=') >= 0) {
const parts = item.split(',')
const parts = item.split(',');
let importItem = {
directory: '/',
skylink: '',
@@ -106,7 +111,9 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
part = part.trim();
const pair = part.split('=');
if (pair.length !== 2) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
throw new Error(
'Invalid syntax for import: directory="",skylink="",token=""'
);
}
importItem = {
...importItem,
@@ -114,7 +121,9 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
};
}
if (!importItem.skylink) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
throw new Error(
'Invalid syntax for import: directory="",skylink="",token=""'
);
}
importsArray.push(importItem);
} else if (item.length > 0) {
@@ -136,38 +145,54 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
this.props.notifyError(e);
}
}
}
};
onImportSkylinksReply = (_, arg) => {
this.props.notifyApplicationBusy(false);
if (arg.data.Success) {
const failedImportsArray = this.state.imports_array.filter(i => {
const failedImportsArray = this.state.imports_array.filter((i) => {
return arg.data.Result.failed.includes(i.skylink);
});
const count = this.state.imports_array.length;
this.setState({
import_text: failedImportsArray.length > 0 ? JSON.stringify(failedImportsArray, null, 2) : '',
this.setState(
{
import_text:
failedImportsArray.length > 0
? JSON.stringify(failedImportsArray, null, 2)
: '',
imports_array: [],
second_stage: false,
}, () => {
},
() => {
if (failedImportsArray.length > 0) {
this.props.notifyError(`Failed to import ${failedImportsArray.length} item(s)`);
this.props.notifyError(
`Failed to import ${failedImportsArray.length} item(s)`
);
} else {
this.props.notifyInfo('Import Result', `Successfully imported ${count} item(s)`);
this.props.notifyInfo(
'Import Result',
`Successfully imported ${count} item(s)`
);
}
});
}
);
} else {
this.props.notifyError(arg.data.Error);
}
};
render() {
return this.props.AppBusy ? (<div/>) : (
<Box dxDark dxStyle={{
return this.props.AppBusy ? (
<div />
) : (
<Box
dxDark
dxStyle={{
height: 'auto',
padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)'
}}>
width: 'calc(100vw - (var(--default_spacing) * 4)',
}}
>
<div
style={{
float: 'right',
@@ -175,54 +200,79 @@ export default connect(mapStateToProps, mapDispatchToProps)(class SkynetImport e
padding: 0,
marginTop: '-4px',
boxSizing: 'border-box',
display: 'block'
}}>
<a href={'#'}
display: 'block',
}}
>
<a
href={'#'}
onClick={() => this.props.displaySkynetImport(false)}
style={{cursor: 'pointer'}}>X</a>
style={{ cursor: 'pointer' }}
>
X
</a>
</div>
<h1
className={'SkynetImportHeading'}>{this.state.second_stage ? 'Verify Imports' : 'Import List'}</h1>
{
this.state.second_stage ? (
<h1 className={'SkynetImportHeading'}>
{this.state.second_stage ? 'Verify Imports' : 'Import List'}
</h1>
{this.state.second_stage ? (
<ImportList imports_array={this.state.imports_array} />
) : (
<textarea autoFocus={true}
<textarea
autoFocus={true}
className={'SkynetImportTextArea'}
onChange={e => this.setState({
onChange={(e) =>
this.setState({
import_text: e.target.value,
})}
value={this.state.import_text}
rows={10}/>
)
})
}
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>
<div className={'SkynetActionButtons'}>
{
this.state.second_stage ?
<Button buttonStyles={{
buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto'
}} clicked={() => this.setState({
second_stage: false,
})}>{'Back'}</Button> :
null
}
<Button buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto'
width: 'auto',
}}
clicked={this.handleNavigation}>{this.state.second_stage ? 'Import' : 'Next'}</Button>
clicked={this.displaySyntax}
>
Import Syntax...
</Button>
<div className={'SkynetActionButtons'}>
{this.state.second_stage ? (
<Button
buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto',
}}
clicked={() =>
this.setState({
second_stage: false,
})
}
>
{'Back'}
</Button>
) : null}
<Button
buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto',
}}
clicked={this.handleNavigation}
>
{this.state.second_stage ? 'Import' : 'Next'}
</Button>
</div>
</div>
</Box>
);
}
});
}
);

View File

@@ -12,14 +12,14 @@ export default class Password extends Component {
};
componentDidMount() {
if (!this.props.value || (this.props.value.length === 0)) {
if (!this.props.value || this.props.value.length === 0) {
this.setState({
...this.state,
button_text: 'set',
password: '',
password2: '',
show_password: false,
})
});
} else {
this.setState({
...this.state,
@@ -27,7 +27,7 @@ export default class Password extends Component {
password: this.props.value,
password2: '',
show_password: false,
})
});
}
}
@@ -48,20 +48,26 @@ export default class Password extends Component {
this.props.changed(this.state.password);
this.setState({
...this.state,
button_text: this.state.password && this.state.password.length > 0 ? 'clear' : 'set',
button_text:
this.state.password && this.state.password.length > 0
? 'clear'
: 'set',
password2: '',
});
} else {
this.setState({
this.setState(
{
...this.state,
button_text: 'set',
password: '',
password2: '',
}, () => {
},
() => {
if (this.props.mismatchHandler) {
this.props.mismatchHandler();
}
});
}
);
}
break;
@@ -79,7 +85,7 @@ export default class Password extends Component {
}
};
handlePasswordChanged = e => {
handlePasswordChanged = (e) => {
if (this.state.button_text === 'set') {
this.setState({
...this.state,
@@ -93,8 +99,11 @@ export default class Password extends Component {
}
};
handlePasswordKeyUp = e => {
if ((e.keyCode === 13) && ((this.state.button_text === 'confirm') || (this.state.button_text === 'set'))) {
handlePasswordKeyUp = (e) => {
if (
e.keyCode === 13 &&
(this.state.button_text === 'confirm' || this.state.button_text === 'set')
) {
this.handleActionClick();
}
};
@@ -109,27 +118,40 @@ export default class Password extends Component {
render() {
return (
<div className={'PasswordOwner'} style={{ ...this.props.style }}>
{
this.props.readOnly ? null : <a href={'#'}
{this.props.readOnly ? null : (
<a
href={'#'}
className={'PasswordLink'}
onClick={this.handleActionClick}>
onClick={this.handleActionClick}
>
<u>{this.state.button_text}</u>
</a>
}
<input autoFocus={this.props.autoFocus}
)}
<input
autoFocus={this.props.autoFocus}
readOnly={this.props.readOnly}
className={'PasswordInput'}
disabled={this.props.readOnly || (this.state.button_text === 'clear')}
disabled={this.props.readOnly || this.state.button_text === 'clear'}
onChange={this.handlePasswordChanged}
onKeyUp={this.handlePasswordKeyUp}
type={this.state.show_password ? 'text' : 'password'}
value={(this.state.button_text === 'confirm') ? this.state.password2 : this.state.password}/>
<a href={'#'}
value={
this.state.button_text === 'confirm'
? this.state.password2
: this.state.password
}
/>
<a
href={'#'}
className={'PasswordShowHide'}
onClick={this.handleShowHideClick}>
<FontAwesomeIcon icon={this.state.show_password ? faEye : faEyeSlash} fixedWidth/>
onClick={this.handleShowHideClick}
>
<FontAwesomeIcon
icon={this.state.show_password ? faEye : faEyeSlash}
fixedWidth
/>
</a>
</div>
);
}
};
}

View File

@@ -8,6 +8,8 @@ const spawn = require('child_process').spawn;
const Constants = require('./constants');
const RandomString = require('randomstring');
const IS_64BIT = process.arch.indexOf('64') >= 0;
let vcRuntimeExists;
const _vcRuntimeExists = () => {
return new Promise((resolve, reject) => {
@@ -20,11 +22,13 @@ const _vcRuntimeExists = () => {
const cmd = path.join(process.env.windir, 'system32', 'reg.exe');
const args = [
'QUERY',
'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
IS_64BIT
? 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
: 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
];
_execProcessGetOutput(cmd, null, args)
.then(lines => {
const parseLine = index => {
.then((lines) => {
const parseLine = (index) => {
if (index < lines.length) {
const line = lines[index];
if (line.startsWith('HKEY_LOCAL_MACHINE\\')) {
@@ -35,22 +39,29 @@ const _vcRuntimeExists = () => {
args2.push('/t');
args2.push('REG_SZ');
_execProcessGetOutput(cmd, null, args2)
.then(lines => {
.then((lines) => {
const value = lines[2]
.trim()
.substr(args2[3].length)
.trim()
.substr(6)
.trim();
if (value.includes(
'Microsoft Visual C++ 2015-2019 Redistributable (x64)')) {
if (
value.includes(
IS_64BIT
? 'Microsoft Visual C++ 2015-2019 Redistributable (x64)'
: 'Microsoft Visual C++ 2015-2019 Redistributable (x32)'
)
) {
vcRuntimeExists = true;
resolve(true);
} else {
parseLine(++index);
}
})
.catch(() => { parseLine(++index); });
.catch(() => {
parseLine(++index);
});
} else {
parseLine(++index);
}
@@ -60,27 +71,32 @@ const _vcRuntimeExists = () => {
};
parseLine(0);
})
.catch(err => { reject(err); });
.catch((err) => {
reject(err);
});
}
}
});
};
// https://stackoverflow.com/questions/19531453/transform-file-directory-structure-into-tree-in-javascript
const _createTreeNodes = fileList => {
let tree = {}
const _createTreeNodes = (fileList) => {
let tree = {};
const directorySort = (a, b) => {
return !!a.directory === !!b.directory ? a.name.localeCompare(b.name)
: a.directory ? -1 : 1;
return !!a.directory === !!b.directory
? a.name.localeCompare(b.name)
: a.directory
? -1
: 1;
};
const addNode =
obj => {
const addNode = (obj) => {
let fullPath;
const idx = obj.skylink.indexOf('/');
if (idx > -1) {
fullPath = path.join(obj.directory, obj.skylink.substr(idx + 1))
fullPath = path
.join(obj.directory, obj.skylink.substr(idx + 1))
.replace(/\\/g, '/');
} else {
fullPath = path.join(obj.directory, obj.filename).replace(/\\/g, '/');
@@ -100,28 +116,27 @@ const _createTreeNodes = fileList => {
ptr[pathParts[i]].children = ptr[pathParts[i]].children || {};
ptr = ptr[pathParts[i]].children;
}
}
};
const objectToArray =
node => {
Object.keys(node || {}).map((k) => {
const objectToArray = (node) => {
Object.keys(node || {}).forEach((k) => {
if (node[k].children) {
objectToArray(node[k])
objectToArray(node[k]);
}
})
});
if (node.children) {
node.children = Object.values(node.children);
node.children.forEach(objectToArray)
node.children.forEach(objectToArray);
node.children = node.children.sort(directorySort);
}
}
};
fileList.map(addNode);
objectToArray(tree);
return Object.values(tree).sort(directorySort);
};
const _exportAllSkylinks = version => {
const _exportAllSkylinks = (version) => {
return new Promise((resolve, reject) => {
const repertoryExec = _getRepertoryExec(version);
const processOptions = {
@@ -137,13 +152,19 @@ const _exportAllSkylinks = version => {
let result = '';
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.stderr.on('data', (d) => { result += d; });
process.stderr.on('data', (d) => {
result += d;
});
process.on('exit', code => {
process.on('exit', (code) => {
if (code === 0) {
result = result.substr(result.indexOf('{'));
resolve(JSON.parse(result));
@@ -169,9 +190,13 @@ const _executeProcess = (command, working, args = []) => {
const process = new spawn(command, args, processOptions);
const pid = process.pid;
process.on('error', (err) => { reject(err, pid); });
process.on('error', (err) => {
reject(err, pid);
});
process.on('exit', (code) => { resolve(code); });
process.on('exit', (code) => {
resolve(code);
});
process.unref();
});
@@ -181,7 +206,7 @@ const _execProcessGetOutput = (cmd, working, args) => {
return new Promise((resolve, reject) => {
let processOptions = {
env: process.env,
stdio : [ 'ignore', 'pipe', 'pipe' ]
stdio: ['ignore', 'pipe', 'pipe'],
};
if (working) {
processOptions.cwd = working;
@@ -189,9 +214,13 @@ const _execProcessGetOutput = (cmd, working, args) => {
const proc = spawn(cmd, args, processOptions);
let output = '';
proc.stdout.on('data', data => { output += data.toString(); });
proc.stdout.on('data', (data) => {
output += data.toString();
});
proc.on('error', (err) => { reject(err); });
proc.on('error', (err) => {
reject(err);
});
proc.on('exit', () => {
const lines = output.replace(/\r\n/g, '\n').split('\n');
@@ -202,8 +231,9 @@ const _execProcessGetOutput = (cmd, working, args) => {
});
};
const _getDataDirectory =
() => { return _resolvePath(Constants.DATA_LOCATIONS[os.platform()]); };
const _getDataDirectory = () => {
return _resolvePath(Constants.DATA_LOCATIONS[os.platform()]);
};
const _getRepertoryDirectory = () => {
return _resolvePath(Constants.REPERTORY_LOCATIONS[os.platform()]);
@@ -219,23 +249,25 @@ const _getDefaultRepertoryArgs = (provider, remote, s3) => {
} else if (remote) {
args.push('-rm');
args.push(provider.substr(6));
} else if (Constants.PROVIDER_ARG[providerLower] &&
(Constants.PROVIDER_ARG[providerLower].length > 0)) {
} else if (
Constants.PROVIDER_ARG[providerLower] &&
Constants.PROVIDER_ARG[providerLower].length > 0
) {
args.push(Constants.PROVIDER_ARG[providerLower]);
}
return args;
};
const _getRepertoryExec = version => {
const _getRepertoryExec = (version) => {
return {
cmd : (os.platform() === 'win32') ? 'repertory.exe' : './repertory',
cmd: os.platform() === 'win32' ? 'repertory.exe' : './repertory',
working: path.join(_getDataDirectory(), version),
};
};
const _removeDirectoryRecursively = dir => {
const _removeDirectoryRecursively = (dir) => {
if (fs.existsSync(dir)) {
fs.readdirSync(dir).forEach(file => {
fs.readdirSync(dir).forEach((file) => {
const curPath = path.join(dir, file);
if (fs.lstatSync(curPath).isDirectory()) {
module.exports.removeDirectoryRecursively(curPath);
@@ -247,9 +279,11 @@ const _removeDirectoryRecursively = dir => {
}
};
const _resolvePath = str => {
const _resolvePath = (str) => {
if (os.platform() === 'win32') {
return str.replace(/%([^%]+)%/g, (_, n) => { return process.env[n]; });
return str.replace(/%([^%]+)%/g, (_, n) => {
return process.env[n];
});
} else {
return str.replace('~', os.homedir());
}
@@ -277,25 +311,30 @@ module.exports.checkDaemonVersion = (version, provider) => {
args.push('-cv');
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', err => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.on('exit', code => { resolve(code); });
process.on('exit', (code) => {
resolve(code);
});
process.unref();
});
};
module.exports.cleanupOldReleases = versionList => {
module.exports.cleanupOldReleases = (versionList) => {
return new Promise((resolve, reject) => {
try {
if (versionList && versionList.length > 0) {
const dataDir = _getDataDirectory();
const directoryList = fs.readdirSync(dataDir, {withFileTypes : true})
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent);
const directoryList = fs
.readdirSync(dataDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent);
const removeList =
directoryList.filter(dirent => !versionList.includes(dirent.name))
.map(dirent => dirent.name);
const removeList = directoryList
.filter((dirent) => !versionList.includes(dirent.name))
.map((dirent) => dirent.name);
for (const dir of removeList) {
try {
@@ -314,10 +353,14 @@ module.exports.cleanupOldReleases = versionList => {
};
module.exports.createSignatureFiles = (signature, publicKey) => {
const fileName1 =
RandomString.generate({length : 12, charset : 'alphabetic'});
const fileName2 =
RandomString.generate({length : 12, charset : 'alphabetic'});
const fileName1 = RandomString.generate({
length: 12,
charset: 'alphabetic',
});
const fileName2 = RandomString.generate({
length: 12,
charset: 'alphabetic',
});
const signatureFile = path.join(os.tmpdir(), fileName1 + '.sig');
const publicKeyFile = path.join(os.tmpdir(), fileName2 + '.pub');
@@ -343,7 +386,7 @@ module.exports.detectRepertoryMounts = (version, providerList) => {
PID: -1,
};
}
const grabStatus = index => {
const grabStatus = (index) => {
if (index >= providerList.length) {
resolve(mountState);
} else {
@@ -361,23 +404,32 @@ module.exports.detectRepertoryMounts = (version, providerList) => {
!Constants.PROVIDER_LIST.includes(provider) &&
provider.toLowerCase().startsWith('remote'),
!Constants.PROVIDER_LIST.includes(provider) &&
provider.toLowerCase().startsWith('s3'));
provider.toLowerCase().startsWith('s3')
);
args.push('-status');
const process = new spawn(repertoryExec.cmd, args, processOptions);
let result = '';
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.on('exit', () => {
mountState[provider] =
_tryParse(result, defaultData)[provider] || defaultData;
if (mountState[provider].Active &&
((mountState[provider].Location === 'elevating') ||
(mountState[provider].Location === ''))) {
setTimeout(() => { grabStatus(index); }, 2000);
if (
mountState[provider].Active &&
(mountState[provider].Location === 'elevating' ||
mountState[provider].Location === '')
) {
setTimeout(() => {
grabStatus(index);
}, 2000);
} else {
grabStatus(++index);
}
@@ -389,8 +441,12 @@ module.exports.detectRepertoryMounts = (version, providerList) => {
});
};
module.exports.downloadFile = (url, destination, progressCallback,
completeCallback) => {
module.exports.downloadFile = (
url,
destination,
progressCallback,
completeCallback
) => {
try {
if (fs.existsSync(destination)) {
fs.unlinkSync(destination);
@@ -404,7 +460,7 @@ module.exports.downloadFile = (url, destination, progressCallback,
.get(url, {
responseType: 'stream',
})
.then(response => {
.then((response) => {
try {
const total = parseInt(response.headers['content-length'], 10);
if (total === 0) {
@@ -417,7 +473,7 @@ module.exports.downloadFile = (url, destination, progressCallback,
stream.write(Buffer.from(chunk));
downloaded += chunk.length;
if (progressCallback) {
progressCallback((downloaded / total * 100.0).toFixed(2));
progressCallback(((downloaded / total) * 100.0).toFixed(2));
}
});
@@ -427,32 +483,39 @@ module.exports.downloadFile = (url, destination, progressCallback,
completeCallback(new Error('Received 0 bytes'));
} else if (downloaded !== total) {
completeCallback(
new Error('Received incorrect number of bytes'));
new 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 => { completeCallback(error); });
.catch((error) => {
completeCallback(error);
});
};
module.exports.executeAndWait = (command, ignoreResult) => {
return new Promise((resolve, reject) => {
const retryExecute = (count, lastError) => {
if (++count <= 5) {
exec(command, error => {
exec(command, (error) => {
if (error) {
if (!ignoreResult && (error.code === 1)) {
setTimeout(() => { retryExecute(count, error); }, 1000);
if (!ignoreResult && error.code === 1) {
setTimeout(() => {
retryExecute(count, error);
}, 1000);
} else {
reject(error);
}
@@ -472,7 +535,8 @@ module.exports.executeAsync = (command, args = []) => {
return new Promise((resolve, reject) => {
const launchProcess = (count, timeout) => {
let cmd = path.basename(command);
const working = cmd.length === command.length
const working =
cmd.length === command.length
? null
: command.substr(0, command.length - cmd.length);
let processOptions = {
@@ -489,14 +553,19 @@ module.exports.executeAsync = (command, args = []) => {
const process = new spawn(cmd, args, processOptions);
const pid = process.pid;
process.on('error', err => {
process.on('error', (err) => {
if (++count === 5) {
reject(err, pid);
} else {
clearTimeout(timeout);
setTimeout(
() => launchProcess(count, setTimeout(() => resolve(), 3000)),
1000);
() =>
launchProcess(
count,
setTimeout(() => resolve(), 3000)
),
1000
);
}
});
@@ -507,8 +576,13 @@ module.exports.executeAsync = (command, args = []) => {
} else {
clearTimeout(timeout);
setTimeout(
() => launchProcess(count, setTimeout(() => resolve(), 3000)),
1000);
() =>
launchProcess(
count,
setTimeout(() => resolve(), 3000)
),
1000
);
}
}
});
@@ -516,11 +590,14 @@ module.exports.executeAsync = (command, args = []) => {
process.unref();
};
launchProcess(0, setTimeout(() => resolve(), 3000));
launchProcess(
0,
setTimeout(() => resolve(), 3000)
);
});
};
module.exports.executeScript = script => {
module.exports.executeScript = (script) => {
return new Promise((resolve, reject) => {
const processOptions = {
detached: false,
@@ -534,17 +611,29 @@ module.exports.executeScript = script => {
const process = new spawn(command, args, processOptions);
let result = '';
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.on('exit', () => { resolve(result); });
process.on('exit', () => {
resolve(result);
});
process.unref();
});
};
module.exports.executeMount =
(version, provider, remote, s3, location, exitCallback) => {
module.exports.executeMount = (
version,
provider,
remote,
s3,
location,
exitCallback
) => {
return new Promise((resolve) => {
const repertoryExec = _getRepertoryExec(version);
const processOptions = {
@@ -556,7 +645,7 @@ module.exports.executeMount =
const args = _getDefaultRepertoryArgs(provider, remote, s3);
if ((os.platform() === 'linux') || (os.platform() === 'darwin')) {
if (os.platform() === 'linux' || os.platform() === 'darwin') {
args.push('-o');
args.push('big_writes');
args.push('-f');
@@ -569,7 +658,9 @@ module.exports.executeMount =
let process = new spawn(repertoryExec.cmd, args, processOptions);
const pid = process.pid;
const timeout = setTimeout(() => { resolve(pid); }, 3000);
const timeout = setTimeout(() => {
resolve(pid);
}, 3000);
process.on('error', (err) => {
clearTimeout(timeout);
@@ -602,13 +693,19 @@ module.exports.exportSkylinks = (version, paths) => {
let result = '';
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.stderr.on('data', (d) => { result += d; });
process.stderr.on('data', (d) => {
result += d;
});
process.on('exit', code => {
process.on('exit', (code) => {
if (code === 0) {
result = result.substr(result.indexOf('{'));
resolve(JSON.parse(result));
@@ -637,9 +734,13 @@ module.exports.getConfig = (version, provider, remote, s3) => {
const process = new spawn(repertoryExec.cmd, args, processOptions);
let result = '';
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.on('exit', () => {
const lines = result.replace(/\r\n/g, '\n').split('\n');
@@ -675,11 +776,17 @@ module.exports.getConfigTemplate = (version, provider, remote, s3) => {
const process = new spawn(repertoryExec.cmd, args, processOptions);
let result = '';
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.on('exit', () => { resolve(JSON.parse(result)); });
process.on('exit', () => {
resolve(JSON.parse(result));
});
process.unref();
});
};
@@ -688,7 +795,7 @@ module.exports.getDataDirectory = _getDataDirectory;
module.exports.getRepertoryDirectory = _getRepertoryDirectory;
module.exports.getMissingDependencies = dependencies => {
module.exports.getMissingDependencies = (dependencies) => {
return new Promise((resolve, reject) => {
if (!dependencies) {
reject(new Error('Dependency list is invalid'));
@@ -708,7 +815,7 @@ module.exports.getMissingDependencies = dependencies => {
if (index >= dep.registry.length) {
if (dep.display === 'VC Runtime 2015-2019') {
_vcRuntimeExists()
.then(exists => {
.then((exists) => {
if (!exists) {
missing.push(dep);
}
@@ -717,7 +824,7 @@ module.exports.getMissingDependencies = dependencies => {
.catch(() => {
missing.push(dep);
resolveIfComplete();
})
});
} else {
missing.push(dep);
resolveIfComplete();
@@ -769,8 +876,12 @@ module.exports.getMissingDependencies = dependencies => {
} else {
for (const dep of dependencies) {
try {
if (!(fs.lstatSync(dep.file).isFile() ||
fs.lstatSync(dep.file).isSymbolicLink())) {
if (
!(
fs.lstatSync(dep.file).isFile() ||
fs.lstatSync(dep.file).isSymbolicLink()
)
) {
missing.push(dep);
}
} catch (e) {
@@ -782,17 +893,21 @@ module.exports.getMissingDependencies = dependencies => {
});
};
module.exports.grabSkynetFileTree = version => {
module.exports.grabSkynetFileTree = (version) => {
return new Promise((resolve, reject) => {
_exportAllSkylinks(version)
.then(results => {
resolve([ {
.then((results) => {
resolve([
{
name: '/',
directory: true,
children: _createTreeNodes(results.success),
} ]);
},
]);
})
.catch(e => { reject(e); });
.catch((e) => {
reject(e);
});
});
};
@@ -813,13 +928,19 @@ module.exports.grabDirectoryItems = (path, version, provider, remote, s3) => {
let result = '';
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.stderr.on('data', (d) => { result += d; });
process.stderr.on('data', (d) => {
result += d;
});
process.on('exit', code => {
process.on('exit', (code) => {
if (code === 0) {
result = result.substr(result.indexOf('{'));
resolve(JSON.parse(result));
@@ -849,13 +970,19 @@ module.exports.setPinned = (path, pinned, version, provider, remote, s3) => {
let result = '';
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.stderr.on('data', (d) => { result += d; });
process.stderr.on('data', (d) => {
result += d;
});
process.on('exit', code => {
process.on('exit', (code) => {
if (code === 0) {
resolve(JSON.parse(result).success);
} else {
@@ -884,13 +1011,19 @@ module.exports.importSkylinks = (version, jsonArray) => {
let result = '';
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => { result += d; });
process.stdout.on('data', (d) => {
result += d;
});
process.stderr.on('data', (d) => { result += d; });
process.stderr.on('data', (d) => {
result += d;
});
process.on('exit', code => {
process.on('exit', (code) => {
if (code === 0) {
result = result.substr(result.indexOf('{'));
resolve(JSON.parse(result));
@@ -904,8 +1037,10 @@ 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 : '.';
@@ -915,19 +1050,21 @@ module.exports.mkDirByPathSync = (targetDir,
try {
fs.mkdirSync(curDir);
} catch (err) {
if (err.code === 'EEXIST') { // curDir already exists!
if (err.code === 'EEXIST') {
// curDir already exists!
return curDir;
}
// To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on
// Windows.
if (err.code === 'ENOENT') { // Throw the original parentDir error on
if (err.code === 'ENOENT') {
// Throw the original parentDir error on
// curDir `ENOENT` failure.
throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
}
const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
if (!caughtErr || (caughtErr && (targetDir === curDir))) {
if (!caughtErr || (caughtErr && targetDir === curDir)) {
throw err; // Throw if it's just the last created dir.
}
}
@@ -936,7 +1073,7 @@ module.exports.mkDirByPathSync = (targetDir,
}, initDir);
};
module.exports.performWindowsUninstall = names => {
module.exports.performWindowsUninstall = (names) => {
return new Promise((resolve, reject) => {
if (os.platform() !== 'win32') {
reject('Windows OS is not being used');
@@ -944,11 +1081,13 @@ module.exports.performWindowsUninstall = names => {
const cmd = path.join(process.env.windir, 'system32', 'reg.exe');
const args = [
'QUERY',
'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
IS_64BIT
? 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
: 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
];
_execProcessGetOutput(cmd, null, args)
.then(lines => {
const parseLine = index => {
.then((lines) => {
const parseLine = (index) => {
if (index < lines.length) {
const line = lines[index];
if (line.startsWith('HKEY_LOCAL_MACHINE\\')) {
@@ -959,7 +1098,7 @@ module.exports.performWindowsUninstall = names => {
args2.push('/t');
args2.push('REG_SZ');
_execProcessGetOutput(cmd, null, args2)
.then(lines => {
.then((lines) => {
const value = lines[2]
.trim()
.substr(args2[3].length)
@@ -969,23 +1108,28 @@ module.exports.performWindowsUninstall = names => {
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)) {
_executeProcess('msiexec.exe', null, [
'/x',
productCode,
'/norestart',
])
.then((code) => {
if (code === 0 || code === 3010 || code === 1641) {
resolve(true);
} else {
reject('[' + value +
'] uninstall failed: ' + code);
reject('[' + value + '] uninstall failed: ' + code);
}
})
.catch(err => { reject(err); });
.catch((err) => {
reject(err);
});
} else {
parseLine(++index);
}
})
.catch(() => { parseLine(++index); });
.catch(() => {
parseLine(++index);
});
} else {
parseLine(++index);
}
@@ -995,7 +1139,9 @@ module.exports.performWindowsUninstall = names => {
};
parseLine(0);
})
.catch(err => { reject(err); });
.catch((err) => {
reject(err);
});
}
});
};
@@ -1004,8 +1150,14 @@ module.exports.removeDirectoryRecursively = _removeDirectoryRecursively;
module.exports.resolvePath = _resolvePath;
module.exports.setConfigValue =
(name, value, provider, remote, s3, version) => {
module.exports.setConfigValue = (
name,
value,
provider,
remote,
s3,
version
) => {
return new Promise((resolve, reject) => {
const repertoryExec = _getRepertoryExec(version);
const processOptions = {
@@ -1022,9 +1174,11 @@ module.exports.setConfigValue =
const process = new spawn(repertoryExec.cmd, args, processOptions);
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.on('exit', code => {
process.on('exit', (code) => {
if (code !== 0) {
reject(new Error('Failed to set configuration value: ' + code));
} else {
@@ -1051,7 +1205,9 @@ module.exports.stopMountProcess = (version, provider, remote, s3) => {
const process = new spawn(repertoryExec.cmd, args, processOptions);
const pid = process.pid;
process.on('error', (err) => { reject(err); });
process.on('error', (err) => {
reject(err);
});
process.on('exit', (code) => {
resolve({
PID: pid,
@@ -1081,18 +1237,20 @@ module.exports.stopMountProcessSync = (version, provider, remote, s3) => {
process.unref();
};
module.exports.testRepertoryBinary = version => {
module.exports.testRepertoryBinary = (version) => {
return new Promise((resolve, reject) => {
const repertoryExec = _getRepertoryExec(version);
_executeProcess(repertoryExec.cmd, repertoryExec.working, ['-dc'])
.then(code => {
.then((code) => {
if (code === 0) {
resolve();
} else {
reject(new Error('Invalid exit code: ' + code));
}
})
.catch(error => { reject(error); });
.catch((error) => {
reject(error);
});
});
};
@@ -1108,7 +1266,7 @@ module.exports.verifyHash = (file, hash) => {
command = 'sha256sum';
args = ['-b', file, '-z'];
} else {
reject(new Error('Platform not supported: ' + os.platform()))
reject(new Error('Platform not supported: ' + os.platform()));
}
if (command) {
execFile(command, args, (err, stdout) => {
@@ -1129,11 +1287,17 @@ module.exports.verifyHash = (file, hash) => {
module.exports.verifySignature = (file, signatureFile, publicKeyFile) => {
return new Promise((resolve, reject) => {
const executeVerify = openssl => {
execFile(openssl,
const executeVerify = (openssl) => {
execFile(
openssl,
[
'dgst', '-sha256', '-verify', publicKeyFile, '-signature',
signatureFile, file
'dgst',
'-sha256',
'-verify',
publicKeyFile,
'-signature',
signatureFile,
file,
],
(err, stdout) => {
if (err) {
@@ -1141,15 +1305,17 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => {
} else {
resolve(stdout);
}
});
}
);
};
if (os.platform() === 'win32') {
const Registry = require('winreg');
const regKey = new Registry({
hive: Registry.HKLM,
key :
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1'
key: IS_64BIT
? 'SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1'
: 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1',
});
regKey.valueExists('InstallLocation', (err, exists) => {
if (err) {
@@ -1163,13 +1329,13 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => {
}
});
} else {
reject(new Error('Failed to locate \'openssl.exe\''));
reject(new Error("Failed to locate 'openssl.exe'"));
}
});
} else if (os.platform() === 'linux') {
executeVerify('openssl');
} else {
reject(new Error('Platform not supported: ' + os.platform()))
reject(new Error('Platform not supported: ' + os.platform()));
}
});
};

View File

@@ -8,28 +8,33 @@ test('verify signature success', () => {
.verifySignature(
path.resolve('test/test_verify_signature.dat'),
path.resolve('test/test_verify_signature.dat.sig'),
path.resolve('blockstorage_dev_public.pem'))
.then(stdout => {
path.resolve('blockstorage_dev_public.pem')
)
.then((stdout) => {
expect(stdout).toBeDefined();
});
});
test('verify signature fail', () => {
return expect(helpers
.verifySignature(
return expect(
helpers.verifySignature(
path.resolve('test/test_verify_signature_fail.dat'),
path.resolve('test/test_verify_signature.dat.sig'),
path.resolve('blockstorage_dev_public.pem')))
.rejects
.toThrow()
path.resolve('blockstorage_dev_public.pem')
)
).rejects.toThrow();
});
test('create temp signature files', () => {
const b64signature = fs.readFileSync(path.resolve('test/test_create_signature.sig.b64'), {encoding: 'utf8'}).replace(/(\r\n|\n|\r)/gm,"");
const data = helpers
.createSignatureFiles(
const b64signature = fs
.readFileSync(path.resolve('test/test_create_signature.sig.b64'), {
encoding: 'utf8',
})
.replace(/(\r\n|\n|\r)/gm, '');
const data = helpers.createSignatureFiles(
b64signature,
Constants.DEV_PUBLIC_KEY);
Constants.DEV_PUBLIC_KEY
);
expect(data).toBeDefined();
expect(data.PublicKeyFile).toBeDefined();
expect(data.SignatureFile).toBeDefined();
@@ -37,22 +42,33 @@ test('create temp signature files', () => {
expect(fs.statSync(data.SignatureFile).isFile()).toBe(true);
expect(fs.statSync(data.PublicKeyFile).isFile()).toBe(true);
const b64signature2 = fs.readFileSync(data.SignatureFile).toString('base64').replace(/(\r\n|\n|\r)/gm,"");
const b64signature2 = fs
.readFileSync(data.SignatureFile)
.toString('base64')
.replace(/(\r\n|\n|\r)/gm, '');
expect(b64signature2).toEqual(b64signature);
expect(fs.readFileSync(data.PublicKeyFile, {encoding: 'utf8'})).toEqual(Constants.DEV_PUBLIC_KEY);
expect(fs.readFileSync(data.PublicKeyFile, { encoding: 'utf8' })).toEqual(
Constants.DEV_PUBLIC_KEY
);
fs.unlinkSync(data.PublicKeyFile);
fs.unlinkSync(data.SignatureFile);
});
test('verify sha56 success', () => {
const hash = fs.readFileSync('test/test_verify_sha256.dat.sha256', {encoding: 'utf8'});
return expect(helpers.verifyHash(path.resolve('test/test_verify_sha256.dat'), hash))
.resolves.toBe(hash)
const hash = fs.readFileSync('test/test_verify_sha256.dat.sha256', {
encoding: 'utf8',
});
return expect(
helpers.verifyHash(path.resolve('test/test_verify_sha256.dat'), hash)
).resolves.toBe(hash);
});
test('verify sha56 fail', () => {
const hash = fs.readFileSync('test/test_verify_sha256.dat.sha256', {encoding: 'utf8'});
return expect(helpers.verifyHash(path.resolve('test/test_verify_sha256_fail.dat'), hash))
.rejects.toThrow();
const hash = fs.readFileSync('test/test_verify_sha256.dat.sha256', {
encoding: 'utf8',
});
return expect(
helpers.verifyHash(path.resolve('test/test_verify_sha256_fail.dat'), hash)
).rejects.toThrow();
});

View File

@@ -35,7 +35,8 @@ a {
font-weight: bold;
}
html, body {
html,
body {
height: 100%;
width: 100%;
margin: 0;
@@ -52,7 +53,9 @@ p {
text-align: center;
}
h1, h2, h3 {
h1,
h2,
h3 {
padding: 0;
margin: 0;
font-weight: bold;
@@ -65,7 +68,8 @@ h1 {
color: var(--heading_text_color);
}
h2, h3 {
h2,
h3 {
color: var(--heading_other_text_color);
}
@@ -81,19 +85,23 @@ p {
overflow-y: scroll;
}
.scrollable-content, ::-webkit-scrollbar {
.scrollable-content,
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.scrollable-content, ::-webkit-scrollbar * {
.scrollable-content,
::-webkit-scrollbar * {
background: transparent;
}
.scrollable-content, ::-webkit-scrollbar-thumb {
.scrollable-content,
::-webkit-scrollbar-thumb {
background: var(--control_background_hover) !important;
}
.scrollbar-corner, ::-webkit-scrollbar-corner {
.scrollbar-corner,
::-webkit-scrollbar-corner {
background: transparent;
}

View File

@@ -12,7 +12,7 @@ import {setProviderState} from './redux/actions/mount_actions';
import { setActiveRelease } from './redux/actions/release_version_actions';
import createAppStore from './redux/store/createAppStore';
import * as serviceWorker from './serviceWorker';
import {getIPCRenderer} from './utils';
import { getIPCRenderer } from './utils.jsx';
const Constants = require('./constants');
@@ -45,16 +45,18 @@ if (ipcRenderer) {
store.dispatch(setProviderState(provider, state));
}
store.dispatch(
setActiveRelease(result.data.Release, result.data.Version));
setActiveRelease(result.data.Release, result.data.Version)
);
} else {
store = createAppStore(platformInfo, packageJson.version, {});
}
ReactDOM.render((
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
), document.getElementById('root'));
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
});
ipcRenderer.send(Constants.IPC_Get_State);

View File

@@ -1,13 +1,14 @@
import * as Constants from '../../constants';
import { createAction } from '@reduxjs/toolkit';
import {getIPCRenderer} from '../../utils';
import * as Constants from '../../constants';
import { getIPCRenderer } from '../../utils.jsx';
const ipcRenderer = getIPCRenderer();
let yesNoResolvers = [];
export const confirmYesNo = title => {
return dispatch => {
return new Promise(resolve => {
export const confirmYesNo = (title) => {
return (dispatch) => {
return new Promise((resolve) => {
dispatch(handleConfirmYesNo(true, title, resolve));
});
};
@@ -17,15 +18,12 @@ export const DISPLAY_CONFIRM_YES_NO = 'common/displayConfirmYesNo';
const displayConfirmYesNo = (show, title) => {
return {
type: DISPLAY_CONFIRM_YES_NO,
payload: {
show,
title
},
payload: { show, title },
};
};
export const displaySelectAppPlatform = display => {
return dispatch => {
export const displaySelectAppPlatform = (display) => {
return (dispatch) => {
if (display) {
dispatch(showWindow());
}
@@ -34,14 +32,14 @@ export const displaySelectAppPlatform = display => {
};
};
export const hideConfirmYesNo = confirmed => {
return dispatch => {
export const hideConfirmYesNo = (confirmed) => {
return (dispatch) => {
dispatch(handleConfirmYesNo(false, confirmed));
};
};
const handleConfirmYesNo = (show, titleOrConfirmed, resolve) => {
return dispatch => {
return (dispatch) => {
if (show) {
yesNoResolvers.push(resolve);
dispatch(displayConfirmYesNo(show, titleOrConfirmed));
@@ -57,26 +55,23 @@ export const NOTIFY_APPLICATION_BUSY = 'common/notifyApplicationBusy';
export const notifyApplicationBusy = (busy, transparent) => {
return {
type: NOTIFY_APPLICATION_BUSY,
payload: {
busy,
transparent
},
payload: { busy, transparent },
};
};
export const notifyRebootRequired = createAction('common/notifyRebootRequired');
export const rebootSystem = () => {
return dispatch => {
return (dispatch) => {
dispatch(setApplicationReady(false));
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Reboot_System);
}
}
};
};
export const saveState = () => {
return (dispatch, getState) => {
return (_, getState) => {
const state = getState();
if (state.common.AppReady) {
let currentState = {
@@ -94,9 +89,7 @@ export const saveState = () => {
}
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Save_State, {
State: currentState
});
ipcRenderer.send(Constants.IPC_Save_State, { State: currentState });
}
}
};
@@ -106,7 +99,7 @@ export const setAllowMount = createAction('common/setAllowMount');
export const setApplicationReady = createAction('common/setApplicationReady');
export const SET_DISPLAY_SELECT_APPPLATFORM = 'common/displaySelectAppPlatform';
export const setDisplaySelectAppPlatform = display => {
export const setDisplaySelectAppPlatform = (display) => {
return {
type: SET_DISPLAY_SELECT_APPPLATFORM,
payload: display,
@@ -116,14 +109,14 @@ export const setDisplaySelectAppPlatform = display => {
export const setLinuxAppPlatform = createAction('common/setLinuxAppPlatform');
export const setRebootRequired = () => {
return dispatch => {
return (dispatch) => {
dispatch(showWindow());
dispatch(notifyRebootRequired(true));
};
};
export const showWindow = () => {
return dispatch => {
return (_) => {
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Show_Window);
}
@@ -131,7 +124,7 @@ export const showWindow = () => {
};
export const shutdownApplication = () => {
return dispatch => {
return (dispatch) => {
dispatch(setApplicationReady(false));
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Shutdown);

View File

@@ -1,54 +1,72 @@
import * as Constants from '../../constants';
import { createAction } from '@reduxjs/toolkit';
import {getIPCRenderer} from '../../utils';
import * as Constants from '../../constants';
import { getIPCRenderer } from '../../utils.jsx';
import { notifyError } from './error_actions';
import {
installAndTestRelease,
installDependency,
installRelease,
installUpgrade
installUpgrade,
} from './install_actions';
export const setAllowDownload = createAction('download/setAllowDownload');
export const SET_DOWNLOAD_BEGIN = 'download/setDownloadBegin';
export const setDownloadBegin = (name, type, url) => {
return {
type: SET_DOWNLOAD_BEGIN,
payload: {
name,
type,
url
}
};
return { type: SET_DOWNLOAD_BEGIN, payload: { name, type, url } };
};
export const setDownloadEnd = createAction('download/setDownloadEnd');
export const setDownloadProgress = createAction('download/setDownloadProgress');
export const downloadItem = (name, type, urls, isWinFSP, testVersion, appPlatform) => {
export const downloadItem = (
name,
type,
urls,
isWinFSP,
testVersion,
appPlatform
) => {
return (dispatch, getState) => {
if (!Array.isArray(urls)) {
urls = [urls];
}
const downloadComplete = result => {
const downloadComplete = (result) => {
if (result.Success) {
switch (type) {
case Constants.INSTALL_TYPES.Dependency:
dispatch(installDependency(result.Destination, result.URL, isWinFSP));
dispatch(
installDependency(result.Destination, result.URL, isWinFSP)
);
break;
case Constants.INSTALL_TYPES.Release:
dispatch(installRelease(result.Destination));
break;
case Constants.INSTALL_TYPES.TestRelease:
dispatch(installAndTestRelease(result.Destination, testVersion, appPlatform));
dispatch(
installAndTestRelease(
result.Destination,
testVersion,
appPlatform
)
);
break;
case Constants.INSTALL_TYPES.Upgrade:
//const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]];
// 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;
dispatch(installUpgrade(result.Destination, sha256, signature, !!result.SkipVerification));
dispatch(
installUpgrade(
result.Destination,
sha256,
signature,
!!result.SkipVerification
)
);
break;
default:
dispatch(notifyError('Unknown download type: ' + type));
@@ -62,10 +80,13 @@ export const downloadItem = (name, type, urls, isWinFSP, testVersion, appPlatfor
}
};
const downloadAtIndex = index => {
const downloadAtIndex = (index) => {
const url = urls[index];
const state = getState();
if ((index > 0) || (!state.download.DownloadActive && state.download.AllowDownload)) {
if (
index > 0 ||
(!state.download.DownloadActive && state.download.AllowDownload)
) {
const ipcRenderer = getIPCRenderer();
if (ipcRenderer) {
dispatch(setDownloadBegin(name, type, url));
@@ -75,8 +96,11 @@ export const downloadItem = (name, type, urls, isWinFSP, testVersion, appPlatfor
};
const downloadFileComplete = (_, arg) => {
ipcRenderer.removeListener(Constants.IPC_Download_File_Progress, downloadFileProgress);
if (!arg.data.Success && (++index < urls.length)) {
ipcRenderer.removeListener(
Constants.IPC_Download_File_Progress,
downloadFileProgress
);
if (!arg.data.Success && ++index < urls.length) {
downloadAtIndex(index);
} else {
downloadComplete(arg.data);
@@ -84,8 +108,14 @@ export const downloadItem = (name, type, urls, isWinFSP, testVersion, appPlatfor
}
};
ipcRenderer.on(Constants.IPC_Download_File_Progress, downloadFileProgress);
ipcRenderer.once(Constants.IPC_Download_File_Complete, downloadFileComplete);
ipcRenderer.on(
Constants.IPC_Download_File_Progress,
downloadFileProgress
);
ipcRenderer.once(
Constants.IPC_Download_File_Complete,
downloadFileComplete
);
ipcRenderer.send(Constants.IPC_Download_File, {
Filename: name,

View File

@@ -1,7 +1,4 @@
import {
showWindow,
shutdownApplication
} from './common_actions';
import { showWindow, shutdownApplication } from './common_actions';
let ErrorActions = [];
@@ -36,13 +33,13 @@ export const dismissError = () => {
};
export const dismissInfo = () => {
return dispatch => {
return (dispatch) => {
dispatch(clearInfo());
};
};
export const notifyError = (msg, critical, callback) => {
return dispatch => {
return (dispatch) => {
ErrorActions = [callback, ...ErrorActions];
msg = msg ? msg.toString() : 'Unknown Error';
dispatch(setErrorInfo(msg, critical));
@@ -51,7 +48,7 @@ export const notifyError = (msg, critical, callback) => {
};
export const notifyInfo = (title, msg) => {
return dispatch => {
return (dispatch) => {
title = title ? title.toString() : 'Information';
msg = msg ? msg.toString() : '';
dispatch(setInfo(title, msg));
@@ -60,23 +57,10 @@ export const notifyInfo = (title, msg) => {
export const SET_ERROR_INFO = 'error/setErrorInfo';
export const setErrorInfo = (msg, critical) => {
return {
type: SET_ERROR_INFO,
payload: {
msg,
critical
}
}
return { type: SET_ERROR_INFO, payload: { msg, critical } };
};
export const SET_INFO = 'error/setInfo';
export const setInfo = (title, msg) => {
return {
type: SET_INFO,
payload: {
title,
msg
}
}
return { type: SET_INFO, payload: { title, msg } };
};

View File

@@ -1,18 +1,8 @@
import * as Constants from '../../constants';
import { createAction } from '@reduxjs/toolkit';
import {
getIPCRenderer,
getSelectedVersionFromState
} from '../../utils';
import {notifyError} from './error_actions';
import {downloadItem, setAllowDownload} from './download_actions';
import {
loadReleases,
setActiveRelease,
setInstalledVersion,
setReleaseUpgradeAvailable,
setNewReleasesAvailable2,
} from './release_version_actions';
import * as Constants from '../../constants';
import { getIPCRenderer, getSelectedVersionFromState } from '../../utils.jsx';
import {
confirmYesNo,
displaySelectAppPlatform,
@@ -21,18 +11,28 @@ import {
setLinuxAppPlatform,
setRebootRequired,
showWindow,
shutdownApplication
shutdownApplication,
} from './common_actions';
import { downloadItem, setAllowDownload } from './download_actions';
import { notifyError } from './error_actions';
import { unmountAll } from './mount_actions';
import {
loadReleases,
setActiveRelease,
setInstalledVersion,
setNewReleasesAvailable2,
setReleaseUpgradeAvailable,
} from './release_version_actions';
const ipcRenderer = getIPCRenderer();
export const checkInstalled = (dependencies, version) => {
return (dispatch, getState) => {
const checkInstalledComplete = (event, arg) => {
const checkInstalledComplete = (_, arg) => {
const result = arg.data;
const updateState = () => {
const installedVersion = result.Success && result.Exists ? result.Version : 'none';
const installedVersion =
result.Success && result.Exists ? result.Version : 'none';
const state = getState();
const release = state.relver.Release;
@@ -40,7 +40,9 @@ export const checkInstalled = (dependencies, version) => {
let upgradeAvailable = false;
if (installedVersion !== 'none') {
const latestVersion = state.relver.VersionLookup[Constants.RELEASE_TYPES[release]].length - 1;
const latestVersion =
state.relver.VersionLookup[Constants.RELEASE_TYPES[release]]
.length - 1;
if (version === -1) {
version = latestVersion;
dispatch(setActiveRelease(release, version));
@@ -57,14 +59,18 @@ export const checkInstalled = (dependencies, version) => {
const autoInstallRelease = getState().install.AutoInstallRelease;
dispatch(setAutoInstallRelease(false));
if (result.Dependencies && (result.Dependencies.length > 0)) {
if (result.Dependencies && result.Dependencies.length > 0) {
dispatch(showWindow());
} else if ((installedVersion === 'none') && autoInstallRelease) {
} else if (installedVersion === 'none' && autoInstallRelease) {
dispatch(setAllowMount(false));
const versionString = getState().relver.VersionLookup[Constants.RELEASE_TYPES[release]][version];
const versionString = getState().relver.VersionLookup[
Constants.RELEASE_TYPES[release]
][version];
const urls = getState().relver.LocationsLookup[versionString].urls;
const fileName = versionString + '.zip';
dispatch(downloadItem(fileName, Constants.INSTALL_TYPES.Release, urls));
dispatch(
downloadItem(fileName, Constants.INSTALL_TYPES.Release, urls)
);
}
};
@@ -75,7 +81,10 @@ export const checkInstalled = (dependencies, version) => {
}
};
ipcRenderer.once(Constants.IPC_Check_Installed_Reply, checkInstalledComplete);
ipcRenderer.once(
Constants.IPC_Check_Installed_Reply,
checkInstalledComplete
);
ipcRenderer.send(Constants.IPC_Check_Installed, {
Dependencies: dependencies,
Version: version,
@@ -89,10 +98,14 @@ export const checkVersionInstalled = () => {
dispatch(setAllowDownload(false));
const selectedVersion = getSelectedVersionFromState(state);
if (selectedVersion && (selectedVersion !== 'unavailable')) {
if (selectedVersion && selectedVersion !== 'unavailable') {
let dependencies = [];
if (state.relver.LocationsLookup[selectedVersion] && state.relver.LocationsLookup[selectedVersion].dependencies) {
dependencies = state.relver.LocationsLookup[selectedVersion].dependencies;
if (
state.relver.LocationsLookup[selectedVersion] &&
state.relver.LocationsLookup[selectedVersion].dependencies
) {
dependencies =
state.relver.LocationsLookup[selectedVersion].dependencies;
}
dispatch(checkInstalled(dependencies, selectedVersion));
} else {
@@ -107,7 +120,7 @@ export const installDependency = (source, url, isWinFSP) => {
if (ipcRenderer && !getState().install.InstallActive) {
dispatch(setInstallActive(Constants.INSTALL_TYPES.Dependency));
const installDependencyComplete = (event, arg) => {
const installDependencyComplete = (_, arg) => {
const result = arg.data;
const handleCompleted = () => {
if (result.RebootRequired) {
@@ -122,13 +135,16 @@ export const installDependency = (source, url, isWinFSP) => {
};
if (result.Success && source.toLowerCase().endsWith('.dmg')) {
const dep = getState().install.MissingDependencies.find(d => {
const dep = getState().install.MissingDependencies.find((d) => {
return d.download === url;
});
const i = setInterval(() => {
const ret = ipcRenderer.sendSync(Constants.IPC_Check_Dependency_Installed + '_sync', {
const ret = ipcRenderer.sendSync(
Constants.IPC_Check_Dependency_Installed + '_sync',
{
File: dep.file,
});
}
);
if (ret.data.Exists) {
clearInterval(i);
@@ -142,7 +158,10 @@ export const installDependency = (source, url, isWinFSP) => {
}
};
ipcRenderer.once(Constants.IPC_Install_Dependency_Reply, installDependencyComplete);
ipcRenderer.once(
Constants.IPC_Install_Dependency_Reply,
installDependencyComplete
);
ipcRenderer.send(Constants.IPC_Install_Dependency, {
Source: source,
URL: url,
@@ -160,7 +179,7 @@ export const installAndTestRelease = (source, version, appPlatform) => {
FilePath: source,
});
ipcRenderer.once(Constants.IPC_Test_Release_Reply, (event, arg) => {
ipcRenderer.once(Constants.IPC_Test_Release_Reply, (_, arg) => {
if (arg.data.Success) {
ipcRenderer.sendSync(Constants.IPC_Set_Linux_AppPlatform, {
AppPlatform: appPlatform,
@@ -177,10 +196,13 @@ export const installAndTestRelease = (source, version, appPlatform) => {
});
ipcRenderer.send(Constants.IPC_Test_Release, {
Version: version,
})
});
};
ipcRenderer.once(Constants.IPC_Extract_Release_Complete, extractReleaseComplete);
ipcRenderer.once(
Constants.IPC_Extract_Release_Complete,
extractReleaseComplete
);
ipcRenderer.send(Constants.IPC_Extract_Release, {
Source: source,
Version: version,
@@ -192,7 +214,10 @@ export const installAndTestRelease = (source, version, appPlatform) => {
export const installReleaseByVersion = (release, version) => {
return (dispatch, getState) => {
const install = () => {
if (getState().download.AllowDownload && !getState().download.DownloadActive) {
if (
getState().download.AllowDownload &&
!getState().download.DownloadActive
) {
dispatch(setAutoInstallRelease(true));
dispatch(setActiveRelease(release, version));
} else {
@@ -202,31 +227,34 @@ export const installReleaseByVersion = (release, version) => {
if (getState().mounts.MountsBusy) {
dispatch(confirmYesNo('Unmount all drives?'))
.then(confirmed => {
.then((confirmed) => {
if (confirmed) {
dispatch(unmountAll(install));
}
})
.catch(error => notifyError(error));
.catch((error) => notifyError(error));
} else {
install();
}
};
};
export const installRelease = source => {
export const installRelease = (source) => {
return (dispatch, getState) => {
if (ipcRenderer && !getState().install.InstallActive) {
dispatch(setInstallActive(Constants.INSTALL_TYPES.Release));
const version = getSelectedVersionFromState(getState());
const extractReleaseComplete = (event, arg) => {
const extractReleaseComplete = (_, arg) => {
ipcRenderer.send(Constants.IPC_Delete_File, {
FilePath: source,
});
if (arg.data.Success) {
localStorage.setItem('previous_releases', localStorage.getItem('releases'));
localStorage.setItem(
'previous_releases',
localStorage.getItem('releases')
);
dispatch(setNewReleasesAvailable2([]));
}
@@ -234,7 +262,10 @@ export const installRelease = source => {
dispatch(checkVersionInstalled());
};
ipcRenderer.once(Constants.IPC_Extract_Release_Complete, extractReleaseComplete);
ipcRenderer.once(
Constants.IPC_Extract_Release_Complete,
extractReleaseComplete
);
ipcRenderer.send(Constants.IPC_Extract_Release, {
Source: source,
Version: version,
@@ -249,23 +280,33 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => {
dispatch(setInstallActive(Constants.INSTALL_TYPES.Upgrade));
dispatch(setApplicationReady(false));
const installUpgradeComplete = (event, arg) => {
const installUpgradeComplete = (_, arg) => {
const result = arg.data;
if (result.Success) {
dispatch(shutdownApplication());
} else {
dispatch(setApplicationReady(true));
dispatch(setInstallComplete(result));
dispatch(notifyError(result.Error, false, () => {
dispatch(
notifyError(
result.Error,
false,
() => {
// TODO Prompt to verify
if (result.AllowSkipVerification) {
dispatch(installUpgrade(source, sha256, signature, true));
}
}, false));
},
false
)
);
}
};
ipcRenderer.once(Constants.IPC_Install_Upgrade_Reply, installUpgradeComplete);
ipcRenderer.once(
Constants.IPC_Install_Upgrade_Reply,
installUpgradeComplete
);
ipcRenderer.send(Constants.IPC_Install_Upgrade, {
Sha256: sha256,
Signature: signature,
@@ -276,9 +317,17 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => {
};
};
export const setAutoInstallRelease = createAction('install/setAutoInstallRelease');
export const setDismissDependencies = createAction('install/setDismissDependencies');
export const setAutoInstallRelease = createAction(
'install/setAutoInstallRelease'
);
export const setDismissDependencies = createAction(
'install/setDismissDependencies'
);
export const setInstallActive = createAction('install/setInstallActive');
export const setInstallTestActive = createAction('install/setInstallTestActive');
export const setInstallTestActive = createAction(
'install/setInstallTestActive'
);
export const setInstallComplete = createAction('install/setInstallComplete');
export const setMissingDependencies = createAction('install/setMissingDependencies');
export const setMissingDependencies = createAction(
'install/setMissingDependencies'
);

View File

@@ -1,7 +1,7 @@
import {createAction} from '@reduxjs/toolkit';
import * as Constants from '../../constants';
import {getIPCRenderer} from '../../utils';
import {getIPCRenderer} from '../../utils.jsx';
import {confirmYesNo, saveState} from './common_actions';
import {notifyError} from './error_actions';
@@ -23,8 +23,10 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
Version: getState().relver.InstalledVersion,
});
} else {
dispatch(
notifyError('Failed to set \'RemoteToken\': ' + arg.data.Error));
dispatch(noti
'Failed to set \'RemoteToken\': '
oken
': " + arg.data.Error));
dispatch(setBusy(false));
}
});
@@ -43,7 +45,14 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
};
};
export const addS3Mount = (name, accessKey, secretKey, region, bucketName, url) => {
export const addS3Mount = (
name,
accessKey,
secretKey,
region,
bucketName,
url
) => {
return (dispatch, getState) => {
const ipcRenderer = getIPCRenderer();
const provider = 'S3' + name;
@@ -60,7 +69,8 @@ export const addS3Mount = (name, accessKey, secretKey, region, bucketName, url)
});
} else {
dispatch(
notifyError('Failed to create S3 instance: ' + arg.data.Error));
notifyError('Failed to create S3 instance: ' + arg.data.Error)
);
dispatch(setBusy(false));
}
});
@@ -95,11 +105,12 @@ export const displayConfiguration = (provider, remote, s3) => {
};
};
export const removeMount = provider => {
return dispatch => {
export const removeMount = (provider) => {
return (dispatch) => {
const isRemote = provider.startsWith('Remote');
dispatch(confirmYesNo('Delete [' + provider.substr(isRemote ? 6 : 2) + ']?'))
.then(confirmed => {
dispatch(
confirmYesNo('Delete [' + provider.substr(isRemote ? 6 : 2) + ']?')
).then((confirmed) => {
if (confirmed) {
dispatch(removeMount2(provider));
}
@@ -107,8 +118,8 @@ export const removeMount = provider => {
};
};
const removeMount2 = provider => {
return dispatch => {
const removeMount2 = (provider) => {
return (dispatch) => {
const ipcRenderer = getIPCRenderer();
ipcRenderer.once(Constants.IPC_Remove_Mount_Reply, (_, arg) => {
if (arg.data.Success) {
@@ -119,7 +130,7 @@ const removeMount2 = provider => {
const isRemote = provider.startsWith('Remote');
ipcRenderer.send(Constants.IPC_Remove_Mount, {
Remote: isRemote,
Name: provider.substr(isRemote ? 6 : 2)
Name: provider.substr(isRemote ? 6 : 2),
});
};
};
@@ -128,13 +139,11 @@ export const removeMount3 = createAction('mounts/removeMount3');
export const RESET_MOUNTS_STATE = 'mounts/resetMountsState';
export const resetMountsState = () => {
return {type: RESET_MOUNTS_STATE, payload: null,}
return {type: RESET_MOUNTS_STATE, payload: null};
};
export const SET_ALLOW_MOUNT = 'mounts/setAllowMount';
export const setAllowMount =
(provider,
allow) => {
export const setAllowMount = (provider, allow) => {
return {type: SET_ALLOW_MOUNT, payload: {provider, allow}};
};
@@ -146,26 +155,22 @@ export const setAutoMountProcessed = (provider, processed) => {
export const setBusy = createAction('mounts/setBusy');
export const SET_MOUNT_STATE = 'mounts/setMountState';
export const setMountState =
(provider,
state) => {
export const setMountState = (provider, state) => {
return {type: SET_MOUNT_STATE, payload: {provider, state}};
};
export const SET_MOUNTED = 'mounts/setMounted';
export const setMounted =
(provider,
mounted) => {
export const setMounted = (provider, mounted) => {
return {type: SET_MOUNTED, payload: {provider, mounted}};
};
export const SET_PROVIDER_STATE = 'mounts/setProviderState';
export const setProviderState = (provider, state) => {
return {type: SET_PROVIDER_STATE, payload: {provider, state}}
return {type: SET_PROVIDER_STATE, payload: {provider, state}};
};
export const unmountAll = completedCallback => {
return dispatch => {
export const unmountAll = (completedCallback) => {
return (dispatch) => {
const ipcRenderer = getIPCRenderer();
const unmountedCallback = () => {
dispatch(resetMountsState());
@@ -173,5 +178,5 @@ export const unmountAll = completedCallback => {
};
ipcRenderer.once(Constants.IPC_Unmount_All_Drives_Reply, unmountedCallback);
ipcRenderer.send(Constants.IPC_Unmount_All_Drives);
}
};
};

View File

@@ -1,4 +1,3 @@
import { createAction } from '@reduxjs/toolkit';
export const displayPinnedManager = createAction('pinned/displayPinnedManager');

View File

@@ -1,23 +1,26 @@
import axios from 'axios';
import * as Constants from '../../constants';
import { createAction } from '@reduxjs/toolkit';
import {notifyError} from './error_actions';
import axios from 'axios';
import * as Constants from '../../constants';
import {
checkNewReleases,
getIPCRenderer,
getNewReleases,
getSelectedVersionFromState,
} from '../../utils.jsx';
import {
saveState,
setAllowMount,
setApplicationReady,
showWindow
showWindow,
} from './common_actions';
import { notifyError } from './error_actions';
import {
checkVersionInstalled,
setDismissDependencies
setDismissDependencies,
} from './install_actions';
import { unmountAll } from './mount_actions';
import {
checkNewReleases,
getIPCRenderer,
getNewReleases, getSelectedVersionFromState
} from '../../utils';
export const CLEAR_UI_UPGRADE = 'relver/clearUIUpgrade';
export const clearUIUpgrade = () => {
@@ -27,12 +30,12 @@ export const clearUIUpgrade = () => {
};
};
const cleanupOldReleases = versionList => {
return dispatch => {
const cleanupOldReleases = (versionList) => {
return (_) => {
const ipcRenderer = getIPCRenderer();
if (ipcRenderer) {
ipcRenderer.sendSync(Constants.IPC_Cleanup_Releases + '_sync', {
version_list: versionList
version_list: versionList,
});
}
};
@@ -42,24 +45,32 @@ export const detectUIUpgrade = () => {
return (dispatch, getState) => {
axios
.get(Constants.UI_RELEASES_URL)
.then(response => {
.then((response) => {
const state = getState();
const appPlatform = state.common.AppPlatform;
const version = state.common.Version;
const data = response.data;
if (data.Versions &&
if (
data.Versions &&
data.Versions[appPlatform] &&
(data.Versions[appPlatform].length > 0) &&
(data.Versions[appPlatform][0] !== version)) {
dispatch(setUIUpgradeData(data.Locations[appPlatform][data.Versions[appPlatform][0]], data.Versions[appPlatform][0]));
data.Versions[appPlatform].length > 0 &&
data.Versions[appPlatform][0] !== version
) {
dispatch(
setUIUpgradeData(
data.Locations[appPlatform][data.Versions[appPlatform][0]],
data.Versions[appPlatform][0]
)
);
if (!state.relver.UpgradeDismissed) {
dispatch(showWindow());
}
} else {
dispatch(clearUIUpgrade());
}
}).catch(() => {
})
.catch(() => {
dispatch(clearUIUpgrade());
});
};
@@ -74,23 +85,34 @@ export const loadReleases = () => {
release = Constants.DEFAULT_RELEASE;
}
let latestVersion = versionLookup[Constants.RELEASE_TYPES[release]].length - 1;
let latestVersion =
versionLookup[Constants.RELEASE_TYPES[release]].length - 1;
let version = state.Version;
if (versionLookup[Constants.RELEASE_TYPES[release]][0] === 'unavailable') {
if (
versionLookup[Constants.RELEASE_TYPES[release]][0] === 'unavailable'
) {
release = Constants.DEFAULT_RELEASE;
version = latestVersion = versionLookup[Constants.RELEASE_TYPES[release]].length - 1
} else if ((version === -1) || !versionLookup[Constants.RELEASE_TYPES[release]][version]) {
version = latestVersion =
versionLookup[Constants.RELEASE_TYPES[release]].length - 1;
} else if (
version === -1 ||
!versionLookup[Constants.RELEASE_TYPES[release]][version]
) {
version = latestVersion;
}
dispatch(setReleaseData(locationsLookup, versionLookup));
const dispatchActions = (processAllowDismiss = true) => {
dispatch(setReleaseUpgradeAvailable((version !== latestVersion)));
dispatch(setReleaseUpgradeAvailable(version !== latestVersion));
dispatch(setApplicationReady(true));
dispatch(detectUIUpgrade());
if (processAllowDismiss) {
dispatch(setAllowDismissDependencies(versionLookup[Constants.RELEASE_TYPES[release]].length > 1));
dispatch(
setAllowDismissDependencies(
versionLookup[Constants.RELEASE_TYPES[release]].length > 1
)
);
}
dispatch(checkVersionInstalled());
@@ -98,16 +120,18 @@ export const loadReleases = () => {
for (const key of Object.keys(locationsLookup)) {
versionList.push(key);
}
dispatch(cleanupOldReleases(versionList))
dispatch(cleanupOldReleases(versionList));
};
if ((version !== state.Version) || (release !== state.Release)) {
dispatch(unmountAll(() => {
if (version !== state.Version || release !== state.Release) {
dispatch(
unmountAll(() => {
dispatch(setActiveRelease(release, version));
dispatchActions(false);
dispatch(showWindow());
dispatch(saveState());
}));
})
);
} else {
dispatchActions();
}
@@ -115,7 +139,7 @@ export const loadReleases = () => {
axios
.get(Constants.RELEASES_URL)
.then(response => {
.then((response) => {
const appPlatform = getState().common.AppPlatform;
const versionLookup = {
Release: response.data.Versions.Release[appPlatform],
@@ -129,14 +153,21 @@ export const loadReleases = () => {
const storedReleases = localStorage.getItem('releases');
let newReleases = [];
if (storedReleases && (storedReleases.length > 0)) {
newReleases = getNewReleases(JSON.parse(storedReleases).VersionLookup, versionLookup, getSelectedVersionFromState(getState()));
if (storedReleases && storedReleases.length > 0) {
newReleases = getNewReleases(
JSON.parse(storedReleases).VersionLookup,
versionLookup,
getSelectedVersionFromState(getState())
);
}
localStorage.setItem('releases', JSON.stringify({
localStorage.setItem(
'releases',
JSON.stringify({
LocationsLookup: locationsLookup,
VersionLookup: versionLookup
}));
VersionLookup: versionLookup,
})
);
dispatchActions(locationsLookup, versionLookup);
dispatch(setNewReleasesAvailable(newReleases));
@@ -144,12 +175,17 @@ export const loadReleases = () => {
dispatch(setNewReleasesAvailable2(newReleases));
localStorage.setItem('previous_releases', storedReleases);
dispatch(showWindow());
} else if ((newReleases = checkNewReleases(getSelectedVersionFromState(getState()))).length > 0) {
} else if (
(newReleases = checkNewReleases(
getSelectedVersionFromState(getState())
)).length > 0
) {
dispatch(setNewReleasesAvailable2(newReleases));
}
}).catch(error => {
})
.catch((error) => {
const releases = localStorage.getItem('releases');
if (releases && (releases.length > 0)) {
if (releases && releases.length > 0) {
const obj = JSON.parse(releases);
const locationsLookup = obj.LocationsLookup;
const versionLookup = obj.VersionLookup;
@@ -166,10 +202,7 @@ export const NOTIFY_ACTIVE_RELEASE = 'relver/notifyActiveRelease';
export const notifyActiveRelease = (release, version) => {
return {
type: NOTIFY_ACTIVE_RELEASE,
payload: {
release: release,
version: version
},
payload: { release: release, version: version },
};
};
@@ -183,7 +216,7 @@ export const setActiveRelease = (release, version) => {
version = -1;
}
const versions = relver.VersionLookup[Constants.RELEASE_TYPES[release]];
dispatch(setAllowDismissDependencies(versions && (versions.length > 1)));
dispatch(setAllowDismissDependencies(versions && versions.length > 1));
dispatch(setDismissDependencies(false));
dispatch(notifyActiveRelease(release, version));
if (common.AppReady) {
@@ -192,12 +225,20 @@ export const setActiveRelease = (release, version) => {
};
};
export const setAllowDismissDependencies = createAction('relver/setAllowDismissDependencies');
export const setDismissNewReleasesAvailable = createAction('relver/setDismissNewReleasesAvailable');
export const setAllowDismissDependencies = createAction(
'relver/setAllowDismissDependencies'
);
export const setDismissNewReleasesAvailable = createAction(
'relver/setDismissNewReleasesAvailable'
);
export const setDismissUIUpgrade = createAction('relver/setDismissUIUpgrade');
export const setInstalledVersion = createAction('relver/setInstalledVersion');
export const setNewReleasesAvailable = createAction('relver/setNewReleasesAvailable');
export const setNewReleasesAvailable2 = createAction('relver/setNewReleasesAvailable2');
export const setNewReleasesAvailable = createAction(
'relver/setNewReleasesAvailable'
);
export const setNewReleasesAvailable2 = createAction(
'relver/setNewReleasesAvailable2'
);
export const SET_RELEASE_DATA = 'relver/setReleaseData';
export const setReleaseData = (locationsLookup, versionLookup) => {
@@ -206,11 +247,13 @@ export const setReleaseData = (locationsLookup, versionLookup)=> {
payload: {
locations: locationsLookup,
versions: versionLookup,
}
}
},
};
};
export const setReleaseUpgradeAvailable = createAction('relver/setReleaseUpgradeAvailable');
export const setReleaseUpgradeAvailable = createAction(
'relver/setReleaseUpgradeAvailable'
);
export const SET_UI_UPGRADE_DATA = 'relver/setUIUpgradeData';
export const setUIUpgradeData = (upgradeData, version) => {
@@ -219,6 +262,6 @@ export const setUIUpgradeData = (upgradeData, version) => {
payload: {
upgrade_data: upgradeData,
version: version,
}
}
},
};
};

View File

@@ -1,16 +1,18 @@
import { createReducer } from '@reduxjs/toolkit';
import {
DISPLAY_CONFIRM_YES_NO,
NOTIFY_APPLICATION_BUSY,
notifyRebootRequired,
SET_DISPLAY_SELECT_APPPLATFORM,
setAllowMount,
setApplicationReady,
setLinuxAppPlatform,
SET_DISPLAY_SELECT_APPPLATFORM
} from '../actions/common_actions';
export const createCommonReducer = (platformInfo, version) => {
return createReducer({
return createReducer(
{
AllowMount: false,
AppBusy: false,
AppBusyTransparent: false,
@@ -22,25 +24,20 @@ export const createCommonReducer = (platformInfo, version) => {
Platform: platformInfo.Platform,
RebootRequired: false,
Version: version,
}, {
},
{
[DISPLAY_CONFIRM_YES_NO]: (state, action) => {
return {
...state,
DisplayConfirmYesNo: action.payload.show,
ConfirmTitle: action.payload.show ? action.payload.title : null,
}
};
},
[SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => {
return {
...state,
DisplaySelectAppPlatform: action.payload,
}
return { ...state, DisplaySelectAppPlatform: action.payload };
},
[setAllowMount]: (state, action) => {
return {
...state,
AllowMount: action.payload,
}
return { ...state, AllowMount: action.payload };
},
[setApplicationReady]: (state, action) => {
return {
@@ -49,10 +46,7 @@ export const createCommonReducer = (platformInfo, version) => {
};
},
[setLinuxAppPlatform]: (state, action) => {
return {
...state,
AppPlatform: action.payload,
}
return { ...state, AppPlatform: action.payload };
},
[NOTIFY_APPLICATION_BUSY]: (state, action) => {
return {
@@ -67,5 +61,6 @@ export const createCommonReducer = (platformInfo, version) => {
RebootRequired: action.payload,
};
},
});
}
);
};

View File

@@ -1,9 +1,10 @@
import { createReducer } from '@reduxjs/toolkit';
import {
setAllowDownload,
SET_DOWNLOAD_BEGIN,
setAllowDownload,
setDownloadEnd,
setDownloadProgress
setDownloadProgress,
} from '../actions/download_actions';
const defaultDownloadState = {
@@ -17,10 +18,12 @@ const defaultDownloadState = {
DownloadType: null,
};
export const downloadReducer = createReducer({
export const downloadReducer = createReducer(
{
...defaultDownloadState,
AllowDownload: false,
}, {
},
{
[setAllowDownload]: (state, action) => {
return {
...state,
@@ -35,7 +38,7 @@ export const downloadReducer = createReducer({
DownloadName: action.payload.name,
DownloadType: action.payload.type,
DownloadURL: action.payload.url,
}
};
},
[setDownloadEnd]: (state, action) => {
return {
@@ -45,9 +48,7 @@ export const downloadReducer = createReducer({
};
},
[setDownloadProgress]: (state, action) => {
return {
...state,
DownloadProgress: action.payload,
return { ...state, DownloadProgress: action.payload };
},
}
}
});
);

View File

@@ -3,31 +3,35 @@ import {
CLEAR_ERROR,
CLEAR_INFO,
SET_ERROR_INFO,
SET_INFO
SET_INFO,
} from '../actions/error_actions';
export const errorReducer = createReducer({
export const errorReducer = createReducer(
{
DisplayError: false,
DisplayInfo: false,
ErrorCritical: false,
ErrorStack: [],
InfoStack: [],
}, {
[CLEAR_ERROR]: state => {
const errorStack = (state.ErrorStack.length > 0) ? state.ErrorStack.slice(1) : [];
return {
...state,
DisplayError: (errorStack.length > 0),
ErrorStack: errorStack,
}
},
[CLEAR_INFO]: state => {
const infoStack = (state.InfoStack.length > 0) ? state.InfoStack.slice(1) : [];
{
[CLEAR_ERROR]: (state) => {
const errorStack =
state.ErrorStack.length > 0 ? state.ErrorStack.slice(1) : [];
return {
...state,
DisplayInfo: (infoStack.length > 0),
DisplayError: errorStack.length > 0,
ErrorStack: errorStack,
};
},
[CLEAR_INFO]: (state) => {
const infoStack =
state.InfoStack.length > 0 ? state.InfoStack.slice(1) : [];
return {
...state,
DisplayInfo: infoStack.length > 0,
InfoStack: infoStack,
}
};
},
[SET_ERROR_INFO]: (state, action) => {
const errorStack = [action.payload.msg, ...state.ErrorStack];
@@ -36,17 +40,14 @@ export const errorReducer = createReducer({
DisplayError: true,
ErrorCritical: state.ErrorCritical || action.payload.critical,
ErrorStack: errorStack,
}
};
},
[SET_INFO]: (state, action) => {
const infoStack = [{
title: action.payload.title,
message: action.payload.msg
}, ...state.InfoStack];
return {
...state,
DisplayInfo: true,
InfoStack: infoStack,
const infoStack = [
{ title: action.payload.title, message: action.payload.msg },
...state.InfoStack,
];
return { ...state, DisplayInfo: true, InfoStack: infoStack };
},
}
}
});
);

View File

@@ -5,10 +5,11 @@ import {
setInstallActive,
setInstallComplete,
setInstallTestActive,
setMissingDependencies
setMissingDependencies,
} from '../actions/install_actions';
export const installReducer = createReducer({
export const installReducer = createReducer(
{
AutoInstallRelease: false,
DismissDependencies: false,
InstallActive: false,
@@ -16,18 +17,13 @@ export const installReducer = createReducer({
InstallTestActive: false,
InstallType: null,
MissingDependencies: [],
}, {
},
{
[setAutoInstallRelease]: (state, action) => {
return {
...state,
AutoInstallRelease: action.payload,
}
return { ...state, AutoInstallRelease: action.payload };
},
[setDismissDependencies]: (state, action) => {
return {
...state,
DismissDependencies: action.payload,
}
return { ...state, DismissDependencies: action.payload };
},
[setInstallActive]: (state, action) => {
return {
@@ -43,18 +39,13 @@ export const installReducer = createReducer({
InstallActive: false,
InstallResult: action.payload,
InstallType: null,
}
};
},
[setInstallTestActive]: (state, action) => {
return {
...state,
InstallTestActive: action.payload,
}
return { ...state, InstallTestActive: action.payload };
},
[setMissingDependencies]: (state, action) => {
return {
...state,
MissingDependencies: action.payload,
return { ...state, MissingDependencies: action.payload };
},
}
}
});
);

View File

@@ -12,49 +12,49 @@ import {
SET_MOUNT_STATE,
SET_MOUNTED,
SET_PROVIDER_STATE,
setBusy
setBusy,
} from '../actions/mount_actions';
export const createMountReducer = state => {
export const createMountReducer = (state) => {
let providerList = [
...Constants.PROVIDER_LIST,
...(state.RemoteMounts || []),
...(state.S3Mounts || []),
];
const providerState = providerList
.map(provider => {
.map((provider) => {
return {
[provider]: {
AutoMount: false,
AutoRestart: false,
MountLocation: '',
}
}
},
};
})
.reduce((map, obj) => {
return {...map, ...obj}
return { ...map, ...obj };
});
const mountState = providerList
.map(provider => {
.map((provider) => {
return {
[provider]: {
AllowMount: false,
DriveLetters: [],
Mounted: false,
}
}
},
};
})
.reduce((map, obj) => {
return {...map, ...obj}
return { ...map, ...obj };
});
const autoMountProcessed =
providerList.map(provider => {
return {[provider]: false,}
const autoMountProcessed = providerList
.map((provider) => {
return { [provider]: false };
})
.reduce((map, obj) => {
return {...map, ...obj}
return { ...map, ...obj };
});
return createReducer(
@@ -89,10 +89,12 @@ export const createMountReducer = state => {
autoMountProcessed[action.payload] = true;
return {
...state, AutoMountProcessed: autoMountProcessed,
MountState: mountState, ProviderState: providerState,
...state,
AutoMountProcessed: autoMountProcessed,
MountState: mountState,
ProviderState: providerState,
RemoteMounts: [...state.RemoteMounts, action.payload],
}
};
},
[addS3Mount2]: (state, action) => {
let mountState = { ...state.MountState };
@@ -113,10 +115,12 @@ export const createMountReducer = state => {
autoMountProcessed[action.payload] = true;
return {
...state, AutoMountProcessed: autoMountProcessed,
MountState: mountState, ProviderState: providerState,
...state,
AutoMountProcessed: autoMountProcessed,
MountState: mountState,
ProviderState: providerState,
S3Mounts: [...state.S3Mounts, action.payload],
}
};
},
[DISPLAY_CONFIGURATION]: (state, action) => {
return {
@@ -136,10 +140,10 @@ export const createMountReducer = state => {
let autoMountProcessed = { ...state.AutoMountProcessed };
delete autoMountProcessed[action.payload];
const remoteMounts =
state.RemoteMounts.filter(i => i !== action.payload);
const s3Mounts =
state.S3Mounts.filter(i => i !== action.payload);
const remoteMounts = state.RemoteMounts.filter(
(i) => i !== action.payload
);
const s3Mounts = state.S3Mounts.filter((i) => i !== action.payload);
return {
...state,
AutoMountProcessed: autoMountProcessed,
@@ -150,7 +154,7 @@ export const createMountReducer = state => {
};
},
[RESET_MOUNTS_STATE]: (state, action) => {
return {...state, MountsBusy: false, MountState: mountState,}
return { ...state, MountsBusy: false, MountState: mountState };
},
[SET_AUTO_MOUNT_PROCESSED]: (state, action) => {
return {
@@ -158,7 +162,7 @@ export const createMountReducer = state => {
AutoMountProcessed: {
...state.AutoMountProcessed,
[action.payload.provider]: action.payload.processed,
}
},
};
},
[SET_ALLOW_MOUNT]: (state, action) => {
@@ -169,13 +173,11 @@ export const createMountReducer = state => {
[action.payload.provider]: {
...state.MountState[action.payload.provider],
AllowMount: action.payload.allow,
}
}
},
},
};
},
[setBusy]:
(state,
action) => {
[setBusy]: (state, action) => {
return { ...state, MountsBusy: action.payload };
},
[SET_MOUNT_STATE]: (state, action) => {
@@ -185,9 +187,9 @@ export const createMountReducer = state => {
...state.MountState,
[action.payload.provider]: {
...state.MountState[action.payload.provider],
...action.payload.state
...action.payload.state,
},
},
}
};
},
[SET_MOUNTED]: (state, action) => {
@@ -198,8 +200,8 @@ export const createMountReducer = state => {
[action.payload.provider]: {
...state.MountState[action.payload.provider],
Mounted: action.payload.mounted,
}
}
},
},
};
},
[SET_PROVIDER_STATE]: (state, action) => {
@@ -209,10 +211,11 @@ export const createMountReducer = state => {
...state.ProviderState,
[action.payload.provider]: {
...state.ProviderState[action.payload.provider],
...action.payload.state
...action.payload.state,
},
},
};
},
}
};
}
});
);
};

View File

@@ -1,13 +1,16 @@
import { createReducer } from '@reduxjs/toolkit';
import { displayPinnedManager } from '../actions/pinned_manager_actions';
export const pinnedManagerReducer = createReducer({
export const pinnedManagerReducer = createReducer(
{
DisplayPinnedManager: false,
}, {
},
{
[displayPinnedManager]: (state, action) => {
return {
...state,
DisplayPinnedManager: action.payload,
};
},
});
}
);

View File

@@ -1,11 +1,10 @@
import { createReducer } from '@reduxjs/toolkit';
import * as Actions from '../actions/release_version_actions';
import * as Constants from '../../constants';
const versionLookup = Constants.RELEASE_TYPES.map(k=> {
return {
[k]: ['unavailable']
};
import * as Constants from '../../constants';
import * as Actions from '../actions/release_version_actions';
const versionLookup = Constants.RELEASE_TYPES.map((k) => {
return { [k]: ['unavailable'] };
}).reduce((map, obj) => {
return {
...map,
@@ -13,7 +12,8 @@ const versionLookup = Constants.RELEASE_TYPES.map(k=> {
};
});
export const releaseVersionReducer = createReducer({
export const releaseVersionReducer = createReducer(
{
AllowDismissDependencies: false,
DismissNewReleasesAvailable: true,
InstalledVersion: 'none',
@@ -28,8 +28,9 @@ export const releaseVersionReducer = createReducer({
UpgradeDismissed: false,
Version: -1,
VersionLookup: versionLookup,
}, {
[Actions.CLEAR_UI_UPGRADE]: state => {
},
{
[Actions.CLEAR_UI_UPGRADE]: (state) => {
return {
...state,
UpgradeAvailable: false,
@@ -42,7 +43,7 @@ export const releaseVersionReducer = createReducer({
return {
...state,
Release: action.payload.release,
Version: action.payload.version
Version: action.payload.version,
};
},
[Actions.setAllowDismissDependencies]: (state, action) => {
@@ -64,10 +65,7 @@ export const releaseVersionReducer = createReducer({
};
},
[Actions.setInstalledVersion]: (state, action) => {
return {
...state,
InstalledVersion: action.payload,
}
return { ...state, InstalledVersion: action.payload };
},
[Actions.setNewReleasesAvailable]: (state, action) => {
return {
@@ -103,5 +101,6 @@ export const releaseVersionReducer = createReducer({
UpgradeVersion: action.payload.version,
UpgradeDismissed: false,
};
},
}
});
);

View File

@@ -1,20 +1,17 @@
import { createReducer } from '@reduxjs/toolkit';
import * as Actions from '../actions/skynet_actions';
export const skynetReducer = createReducer({
export const skynetReducer = createReducer(
{
DisplayExport: false,
DisplayImport: false,
}, {
},
{
[Actions.displaySkynetExport]: (state, action) => {
return {
...state,
DisplayExport: action.payload,
}
return { ...state, DisplayExport: action.payload };
},
[Actions.displaySkynetImport]: (state, action) => {
return {
...state,
DisplayImport: action.payload,
}
return { ...state, DisplayImport: action.payload };
},
});
}
);

View File

@@ -1,12 +1,13 @@
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import { createCommonReducer } from '../reducers/common_reducer';
import { downloadReducer } from '../reducers/download_reducer';
import { errorReducer } from '../reducers/error_reducer';
import { installReducer } from '../reducers/install_reducer';
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 {pinnedManagerReducer} from '../reducers/pinned_manager_reducer'
export default function createAppStore(platformInfo, version, state) {
const reducer = {
@@ -25,6 +26,6 @@ export default function createAppStore(platformInfo, version, state) {
return configureStore({
reducer,
middleware,
devTools: process.env.NODE_ENV !== 'production'
devTools: process.env.NODE_ENV !== 'production',
});
}

View File

@@ -9,12 +9,10 @@ const addListeners = (ipcMain, {closeApplication, setWindowVisibility}) => {
setWindowVisibility(true);
});
ipcMain.on(Constants.IPC_Show_Window + '_sync', event => {
ipcMain.on(Constants.IPC_Show_Window + '_sync', (event) => {
setWindowVisibility(true);
event.returnValue = true;
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -11,10 +11,15 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
Config: data.Data,
});
} else {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, data.Code);
standardIPCReply(
event,
Constants.IPC_Get_Config_Reply,
{},
data.Code
);
}
})
.catch(error => {
.catch((error) => {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error);
});
});
@@ -27,8 +32,13 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
Template: data,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error);
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Get_Config_Template_Reply,
{},
error
);
});
});
@@ -36,12 +46,24 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
const setConfigValue = (i) => {
if (i < data.Items.length) {
helpers
.setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Remote, data.S3, data.Version)
.setConfigValue(
data.Items[i].Name,
data.Items[i].Value,
data.Provider,
data.Remote,
data.S3,
data.Version
)
.then(() => {
setConfigValue(++i);
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}, error);
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Set_Config_Values_Reply,
{},
error
);
});
} else {
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {});
@@ -51,6 +73,4 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -5,43 +5,42 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => {
helpers
.checkDaemonVersion(data.Version, data.Provider)
.then(code => {
.then((code) => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, {
Valid: (code === 0),
Valid: code === 0,
Code: code,
});
})
.catch(e => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, {
.catch((e) => {
standardIPCReply(
event,
Constants.IPC_Check_Daemon_Version_Reply,
{
Valid: false,
}, e);
},
e
);
});
});
ipcMain.on(Constants.IPC_Check_Daemon_Version + '_sync', (event, data) => {
helpers
.checkDaemonVersion(data.Version, data.Provider)
.then(code => {
.then((code) => {
event.returnValue = {
data: {
Success: true,
Valid: (code === 0),
Valid: code === 0,
Code: code,
},
};
})
.catch(e => {
.catch((e) => {
event.returnValue = {
data: {
Error: e.toString(),
Success: false,
Valid: false
},
data: { Error: e.toString(), Success: false, Valid: false },
};
});
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -20,7 +20,9 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}
});
ipcMain.on(Constants.IPC_Check_Dependency_Installed + '_sync', (event, data) => {
ipcMain.on(
Constants.IPC_Check_Dependency_Installed + '_sync',
(event, data) => {
try {
const ls = fs.lstatSync(data.File);
event.returnValue = {
@@ -30,12 +32,11 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
};
} catch (e) {
event.returnValue = {
data: {
Exists: false
},
data: { Exists: false },
};
}
});
}
);
ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => {
if (data.Source.toLowerCase().endsWith('.dmg')) {
@@ -47,11 +48,16 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
URL: data.URL,
});
})
.catch(error=> {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source,
URL: data.URL,
}, error);
},
error
);
});
} else {
const execInstall = () => {
@@ -63,17 +69,22 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
URL: data.URL,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source,
URL: data.URL,
}, error);
},
error
);
});
};
if (data.IsWinFSP) {
helpers
.performWindowsUninstall(Constants.WINFSP_VERSION_NAMES)
.then(uninstalled => {
.then((uninstalled) => {
if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
RebootRequired: true,
@@ -84,11 +95,16 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
execInstall();
}
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source,
URL: data.URL,
}, error);
},
error
);
});
} else {
execInstall();
@@ -97,6 +113,4 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -5,21 +5,29 @@ const path = require('path');
const addListeners = (ipcMain, { standardIPCReply }) => {
ipcMain.on(Constants.IPC_Download_File, (event, data) => {
const destination = path.join(helpers.getDataDirectory(), data.Filename);
helpers.downloadFile(data.URL, destination, (progress) => {
helpers.downloadFile(
data.URL,
destination,
(progress) => {
standardIPCReply(event, Constants.IPC_Download_File_Progress, {
Destination: destination,
Progress: progress,
URL: data.URL,
});
}, error => {
standardIPCReply(event, Constants.IPC_Download_File_Complete, {
},
(error) => {
standardIPCReply(
event,
Constants.IPC_Download_File_Complete,
{
Destination: destination,
URL: data.URL,
}, error);
});
},
error
);
}
);
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -3,17 +3,21 @@ const fs = require('fs');
const addListeners = (ipcMain, { getMainWindow, dialog }) => {
ipcMain.on(Constants.IPC_Browse_Directory + '_sync', (event, data) => {
dialog.showOpenDialog(getMainWindow(), {
dialog.showOpenDialog(
getMainWindow(),
{
defaultPath: data.Location,
properties: ['openDirectory'],
title: data.Title,
}, (filePaths) => {
if (filePaths && (filePaths.length > 0)) {
},
(filePaths) => {
if (filePaths && filePaths.length > 0) {
event.returnValue = filePaths[0];
} else {
event.returnValue = '';
}
});
}
);
});
ipcMain.on(Constants.IPC_Delete_File, (event, data) => {
@@ -21,11 +25,8 @@ const addListeners = (ipcMain, {getMainWindow, dialog}) => {
if (fs.existsSync(data.FilePath)) {
fs.unlinkSync(data.FilePath);
}
} catch (e) {
}
} catch (e) {}
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -10,18 +10,25 @@ let manualMountDetection = {};
let mountedData = {};
let mountedLocations = [];
const clearManualMountDetection = provider => {
const clearManualMountDetection = (provider) => {
if (manualMountDetection[provider]) {
clearInterval(manualMountDetection[provider]);
delete manualMountDetection[provider];
}
};
const monitorMount = (sender, provider, providerList, version, pid, location) => {
const monitorMount = (
sender,
provider,
providerList,
version,
pid,
location
) => {
manualMountDetection[provider] = setInterval(() => {
helpers
.detectRepertoryMounts(version, providerList)
.then(result => {
.then((result) => {
if (result[provider].PID !== pid) {
if (result[provider].PID === -1) {
clearManualMountDetection(provider);
@@ -32,14 +39,14 @@ const monitorMount = (sender, provider, providerList, version, pid, location) =>
Provider: provider,
Error: Error(provider + ' Unmounted').toString(),
Success: false,
}
},
});
} else {
pid = result[provider].PID;
}
}
})
.catch(e => {
.catch((e) => {
console.log(e);
});
}, 6000);
@@ -55,7 +62,12 @@ const unmountAllDrives = () => {
// Unmount all items
for (const mountLocation of mountedLocations) {
const data = mountedData[mountLocation];
helpers.stopMountProcessSync(data.Version, data.Provider, data.Remote, data.S3);
helpers.stopMountProcessSync(
data.Version,
data.Provider,
data.Remote,
data.S3
);
}
mountedLocations = [];
@@ -64,13 +76,13 @@ const unmountAllDrives = () => {
const addListeners = (ipcMain, { setTrayImage, standardIPCReply }) => {
ipcMain.on(Constants.IPC_Check_Mount_Location + '_sync', (event, data) => {
let response = {
Success: true,
Error: ''
};
let response = { Success: true, Error: '' };
try {
if (fs.existsSync(data.Location) && fs.statSync(data.Location).isDirectory()) {
if (
fs.existsSync(data.Location) &&
fs.statSync(data.Location).isDirectory()
) {
if (fs.readdirSync(data.Location).length !== 0) {
response.Success = false;
response.Error = 'Directory not empty: ' + data.Location;
@@ -106,8 +118,7 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
if (Object.keys(locations).length > 0) {
for (const provider of providerList) {
driveInUse = locations[provider].startsWith(drive);
if (driveInUse)
break;
if (driveInUse) break;
}
}
if (!driveInUse) {
@@ -117,17 +128,18 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
driveLetters[provider].push(drive);
}
}
} catch (e) {
}
} catch (e) {}
}
}
if (Object.keys(locations).length > 0) {
for (const provider of providerList) {
if (locations[provider].length > 0) {
if (!driveLetters[provider].find((driveLetter) => {
if (
!driveLetters[provider].find((driveLetter) => {
return driveLetter === locations[provider];
})) {
})
) {
driveLetters[provider].push(locations[provider]);
}
}
@@ -135,17 +147,16 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
}
};
const setImage = locations => {
const setImage = (locations) => {
let driveInUse;
if (Object.keys(locations).length > 0) {
for (const provider of providerList) {
driveInUse = locations[provider] && locations[provider].length > 0;
if (driveInUse)
break;
if (driveInUse) break;
}
}
setTrayImage(driveInUse)
setTrayImage(driveInUse);
};
helpers
@@ -154,7 +165,9 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
let storageData = {};
let locations = {};
for (const provider of providerList) {
storageData[provider] = results[provider] ? results[provider] : {
storageData[provider] = results[provider]
? results[provider]
: {
Active: false,
Location: '',
PID: -1,
@@ -164,7 +177,14 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
if (storageData[provider].PID !== -1) {
expectedUnmount[provider] = false;
if (firstMountCheck) {
monitorMount(event.sender, provider, providerList, data.Version, storageData[provider].PID, storageData[provider].Location);
monitorMount(
event.sender,
provider,
providerList,
data.Version,
storageData[provider].PID,
storageData[provider].Location
);
}
}
}
@@ -185,15 +205,20 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
Provider: provider,
});
})
.catch(error => {
.catch((error) => {
if (os.platform() === 'win32') {
grabDriveLetters({});
}
setImage({});
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
standardIPCReply(
event,
Constants.IPC_Detect_Mount_Reply,
{
DriveLetters: driveLetters[provider],
Provider: provider,
}, error);
},
error
);
});
});
@@ -216,18 +241,30 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
delete mountedData[data.Location];
}
standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, {
standardIPCReply(
event,
Constants.IPC_Unmount_Drive_Reply,
{
Expected: expectedUnmount[data.Provider],
Location: data.Location,
Provider: data.Provider,
Remote: data.Remote,
S3: data.S3,
}, error || Error(data.Provider + ' Unmounted'));
},
error || Error(data.Provider + ' Unmounted')
);
};
helpers
.executeMount(data.Version, data.Provider, data.Remote, data.S3, data.Location, (error, pid) => {
.executeMount(
data.Version,
data.Provider,
data.Remote,
data.S3,
data.Location,
(error, pid) => {
errorHandler(pid, error);
})
}
)
.then(() => {
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
Provider: data.Provider,
@@ -235,7 +272,7 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
S3: data.S3,
});
})
.catch(error => {
.catch((error) => {
errorHandler(-1, error);
});
}
@@ -245,17 +282,31 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
if (data.Remote) {
data.Name = data.Name.replace(':', '_');
}
const dataDirectory = path.resolve(path.join(helpers.getDataDirectory(), '..', data.Remote ? 'remote' : 's3', data.Name));
const dataDirectory = path.resolve(
path.join(
helpers.getDataDirectory(),
'..',
data.Remote ? 'remote' : 's3',
data.Name
)
);
try {
helpers.removeDirectoryRecursively(dataDirectory);
standardIPCReply(event, Constants.IPC_Remove_Mount_Reply, {DataDirectory: dataDirectory});
standardIPCReply(event, Constants.IPC_Remove_Mount_Reply, {
DataDirectory: dataDirectory,
});
} catch (e) {
standardIPCReply(event, Constants.IPC_Remove_Mount_Reply, {DataDirectory: dataDirectory}, e);
standardIPCReply(
event,
Constants.IPC_Remove_Mount_Reply,
{ DataDirectory: dataDirectory },
e
);
}
});
ipcMain.on(Constants.IPC_Unmount_All_Drives, event => {
ipcMain.on(Constants.IPC_Unmount_All_Drives, (event) => {
unmountAllDrives();
standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply);
});
@@ -266,10 +317,10 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
expectedUnmount[data.Provider] = true;
helpers
.stopMountProcess(data.Version, data.Provider, data.Remote, data.S3)
.then(result => {
.then((result) => {
console.log(result);
})
.catch(e => {
.catch((e) => {
console.log(e);
});
});
@@ -277,5 +328,5 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
module.exports = {
addListeners,
unmountAllDrives
unmountAllDrives,
};

View File

@@ -4,46 +4,61 @@ const helpers = require('../../helpers');
const addListeners = (ipcMain, { standardIPCReply }) => {
ipcMain.on(Constants.IPC_Get_Directory_Items, (event, data) => {
helpers
.grabDirectoryItems(data.Path, data.Version, data.Provider, data.Remote, data.S3)
.then(data => {
.grabDirectoryItems(
data.Path,
data.Version,
data.Provider,
data.Remote,
data.S3
)
.then((data) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
Items: data.items,
});
})
.catch(e => {
.catch((e) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
});
});
ipcMain.on(Constants.IPC_Get_Pinned_Files, (event, data) => {
helpers
.grabDirectoryItems(data.Path, data.Version, data.Provider, data.Remote, data.S3)
.then(data => {
.grabDirectoryItems(
data.Path,
data.Version,
data.Provider,
data.Remote,
data.S3
)
.then((data) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
Items: data.items,
});
})
.catch(e => {
.catch((e) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
});
});
ipcMain.on(Constants.IPC_Get_Pinned_Files_Status, (event, data) => {
});
ipcMain.on(Constants.IPC_Get_Pinned_Files_Status, (event, data) => {});
ipcMain.on(Constants.IPC_Set_Pinned + '_sync', (event, data) => {
helpers
.setPinned(data.Path, data.Pinned, data.Version, data.Provider, data.Remote, data.S3)
.then(success => {
.setPinned(
data.Path,
data.Pinned,
data.Version,
data.Provider,
data.Remote,
data.S3
)
.then((success) => {
event.returnValue = success;
})
.catch(e => {
.catch((e) => {
event.returnValue = false;
});
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -10,7 +10,7 @@ const getPlatformOverride = () => {
return _platformOverride;
};
const setPlatformOverride = platformOverride => {
const setPlatformOverride = (platformOverride) => {
_platformOverride = platformOverride;
};
@@ -25,7 +25,7 @@ const addListeners = (ipcMain, {detectScript, saveUiSettings}) => {
const platform = os.platform();
if (platform === 'linux') {
if (_platformOverride && (_platformOverride.length > 0)) {
if (_platformOverride && _platformOverride.length > 0) {
sendResponse(_platformOverride, 'linux');
} else {
const scriptFile = path.join(os.tmpdir(), 'repertory_detect_linux.sh');
@@ -33,25 +33,29 @@ const addListeners = (ipcMain, {detectScript, saveUiSettings}) => {
helpers
.executeScript(scriptFile)
.then(data => {
let appPlatform = data.replace(/(\r\n|\n|\r)/gm, "");
.then((data) => {
let appPlatform = data.replace(/(\r\n|\n|\r)/gm, '');
if (appPlatform === 'unknown') {
helpers
.downloadFile(Constants.LINUX_DETECT_SCRIPT_URL, scriptFile, null, err => {
helpers.downloadFile(
Constants.LINUX_DETECT_SCRIPT_URL,
scriptFile,
null,
(err) => {
if (err) {
sendResponse(appPlatform, platform);
} else {
helpers
.executeScript(scriptFile)
.then(data => {
appPlatform = data.replace(/(\r\n|\n|\r)/gm, "");
.then((data) => {
appPlatform = data.replace(/(\r\n|\n|\r)/gm, '');
sendResponse(appPlatform, platform);
})
.catch(() => {
sendResponse(appPlatform, platform);
});
}
});
}
);
} else {
sendResponse(appPlatform, platform);
}
@@ -75,5 +79,5 @@ const addListeners = (ipcMain, {detectScript, saveUiSettings}) => {
module.exports = {
getPlatformOverride,
setPlatformOverride,
addListeners
addListeners,
};

View File

@@ -13,19 +13,26 @@ const addListeners = (ipcMain, {getCleanupReleases, standardIPCReply}) => {
.then((dependencies) => {
let exists = false;
try {
exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
} catch (e) {
}
exists =
fs.existsSync(destination) &&
fs.lstatSync(destination).isDirectory();
} catch (e) {}
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
Dependencies: dependencies,
Exists: exists,
Version: data.Version,
});
}).catch(error => {
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
})
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Check_Installed_Reply,
{
Dependencies: [],
Version: data.Version,
}, error);
},
error
);
});
});
@@ -45,31 +52,42 @@ const addListeners = (ipcMain, {getCleanupReleases, standardIPCReply}) => {
const stream = fs.createReadStream(data.Source);
stream
.pipe(unzip.Extract({ path: destination }))
.on('error', error => {
.on('error', (error) => {
try {
helpers.removeDirectoryRecursively(destination);
} catch (e) {
}
} catch (e) {}
stream.close();
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
standardIPCReply(
event,
Constants.IPC_Extract_Release_Complete,
{
Source: data.Source,
}, error);
},
error
);
})
.on('finish', () => {
stream.close();
if (os.platform() !== 'win32') {
helpers
.executeAndWait("chmod +x \"" + path.join(destination, 'repertory') + "\"")
.executeAndWait(
'chmod +x "' + path.join(destination, 'repertory') + '"'
)
.then(() => {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Extract_Release_Complete,
{
Source: data.Source,
}, error);
})
},
error
);
});
} else {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
@@ -84,12 +102,10 @@ const addListeners = (ipcMain, {getCleanupReleases, standardIPCReply}) => {
.then(() => {
standardIPCReply(event, Constants.IPC_Test_Release_Reply, {});
})
.catch(error => {
.catch((error) => {
standardIPCReply(event, Constants.IPC_Test_Release_Reply, {}, error);
});
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -5,12 +5,12 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
ipcMain.on(Constants.IPC_Export_Skylinks, (event, data) => {
helpers
.exportSkylinks(data.Version, data.Paths)
.then(result => {
.then((result) => {
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {
Result: result,
});
})
.catch(error => {
.catch((error) => {
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {}, error);
});
});
@@ -18,30 +18,33 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
ipcMain.on(Constants.IPC_Grab_Skynet_Tree, (event, data) => {
helpers
.grabSkynetFileTree(data.Version)
.then(result => {
.then((result) => {
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {
Result: result,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {}, error);
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Grab_Skynet_Tree_Reply,
{},
error
);
});
});
ipcMain.on(Constants.IPC_Import_Skylinks, (event, data) => {
helpers
.importSkylinks(data.Version, data.JsonArray)
.then(result => {
.then((result) => {
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {
Result: result,
});
})
.catch(error => {
.catch((error) => {
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {}, error);
});
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -3,18 +3,19 @@ const fs = require('fs');
const helpers = require('../../helpers');
const path = require('path');
const getDirectories = source => {
const getDirectories = (source) => {
try {
return fs.readdirSync(source, {withFileTypes: true})
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
return fs
.readdirSync(source, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
} catch {
return [];
}
}
};
const addListeners = ipcMain => {
ipcMain.on(Constants.IPC_Get_State, event => {
const addListeners = (ipcMain) => {
ipcMain.on(Constants.IPC_Get_State, (event) => {
helpers.mkDirByPathSync(helpers.getDataDirectory());
let data = {};
@@ -32,14 +33,16 @@ const addListeners = ipcMain => {
AutoMount: false,
AutoRestart: true,
MountLocation: '',
}
};
}
}
data.RemoteMounts = data.RemoteMounts || [];
data.S3Mounts = data.S3Mounts || [];
const remoteItems = getDirectories(path.join(helpers.getRepertoryDirectory(), 'remote'));
const remoteItems = getDirectories(
path.join(helpers.getRepertoryDirectory(), 'remote')
);
for (const dir of remoteItems) {
const name = 'Remote' + dir.replace('_', ':');
if (!data.RemoteMounts || data.RemoteMounts.indexOf(name) === -1) {
@@ -52,7 +55,9 @@ const addListeners = ipcMain => {
}
}
const s3Items = getDirectories(path.join(helpers.getRepertoryDirectory(), 's3'));
const s3Items = getDirectories(
path.join(helpers.getRepertoryDirectory(), 's3')
);
for (const dir of s3Items) {
const name = 'S3' + dir;
if (!data.S3Mounts || data.S3Mounts.indexOf(name) === -1) {
@@ -76,6 +81,4 @@ const addListeners = ipcMain => {
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -11,6 +11,4 @@ const addListeners = (ipcMain, {closeApplication}) => {
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -3,7 +3,10 @@ const fs = require('fs');
const helpers = require('../../helpers');
const os = require('os');
const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCReply}) => {
const addListeners = (
ipcMain,
{ setIsInstalling, unmountAllDrives, standardIPCReply }
) => {
ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => {
let allowSkipVerification = true;
@@ -19,25 +22,34 @@ const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCRe
if (tempPub) {
fs.unlinkSync(tempPub);
}
} catch (e) {
}
} catch (e) {}
};
const errorHandler = err => {
const errorHandler = (err) => {
cleanupFiles();
setIsInstalling(false);
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, {
standardIPCReply(
event,
Constants.IPC_Install_Upgrade_Reply,
{
AllowSkipVerification: allowSkipVerification,
Source: data.Source,
}, err);
},
err
);
};
// TODO Enable verification in 1.0.4
const hasSignature = false;//!data.SkipVerification && data.Signature && (data.Signature.length > 0);
const hasHash = false;//!data.SkipVerification && data.Sha256 && (data.Sha256.length > 0);
const hasSignature = false; //! data.SkipVerification && data.Signature
//! && (data.Signature.length > 0);
const hasHash = false; //! data.SkipVerification && data.Sha256 &&
//! (data.Sha256.length > 0);
if (hasSignature) {
try {
const files = helpers.createSignatureFiles(data.Signature, Constants.DEV_PUBLIC_KEY);
const files = helpers.createSignatureFiles(
data.Signature,
Constants.DEV_PUBLIC_KEY
);
tempPub = files.PublicKeyFile;
tempSig = files.SignatureFile;
} catch (e) {
@@ -72,9 +84,9 @@ const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCRe
.executeAsync(command, args)
.then(() => {
cleanupFiles();
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply)
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply);
})
.catch(error => {
.catch((error) => {
setIsInstalling(false);
errorHandler(error);
});
@@ -87,7 +99,9 @@ const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCRe
executeInstall();
})
.catch(() => {
errorHandler(Error('Failed to verify installation package signature'));
errorHandler(
Error('Failed to verify installation package signature')
);
});
} else if (hasHash) {
helpers
@@ -111,6 +125,4 @@ const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCRe
});
};
module.exports = {
addListeners
};
module.exports = { addListeners };

View File

@@ -57,7 +57,7 @@ export function register(config) {
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
@@ -93,7 +93,7 @@ function registerValidSW(swUrl, config) {
};
};
})
.catch(error => {
.catch((error) => {
console.error('Error during service worker registration:', error);
});
}
@@ -101,7 +101,7 @@ function registerValidSW(swUrl, config) {
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
@@ -109,7 +109,7 @@ function checkValidServiceWorker(swUrl, config) {
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
@@ -128,7 +128,7 @@ function checkValidServiceWorker(swUrl, config) {
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister();
});
}

View File

@@ -1,106 +0,0 @@
import React from 'react';
import * as Constants from './constants';
import Modal from './components/UI/Modal/Modal';
import axios from 'axios';
const ipcRenderer = (!process.versions.hasOwnProperty('electron') && window && window.require) ?
window.require('electron').ipcRenderer :
null;
export const checkNewReleases = selectedVersion => {
let previousReleases = localStorage.getItem('previous_releases');
if (previousReleases) {
previousReleases = JSON.parse(previousReleases).VersionLookup;
let currentReleases = localStorage.getItem('releases');
if (currentReleases) {
currentReleases = JSON.parse(currentReleases).VersionLookup;
return getNewReleases(previousReleases, currentReleases, selectedVersion);
}
}
return [];
};
export const createModalConditionally = (condition, jsx, critical, disableFocusTrap, transparent) => {
const modalProps = {critical: critical, disableFocusTrap: disableFocusTrap, transparent: transparent};
return condition ? (<Modal {...modalProps}>{jsx}</Modal>) : null;
};
export const extractFileNameFromURL = url => {
const parts = url.split('/');
return parts[parts.length - 1];
};
export const formatLinesForDisplay = lines => {
let msg = '';
for (let i = 1; i < lines.length; i++) {
if (i > 1) {
msg += '\n';
}
msg += (lines[i].replace(/(\\#)/gm, '#') + '\n');
}
return msg;
};
export const getChangesForRepertoryVersion = version => {
return new Promise((resolve, reject) => {
const url = 'https://bitbucket.org/blockstorage/repertory/raw/' +
Constants.REPERTORY_BRANCH + '/CHANGELOG.md';
axios
.get(url, {
responseType: 'text',
})
.then(response => {
try {
let found = false;
let ended = false;
let lines = response.data
.replace(/(\r\n)/gm, '\n')
.split('\n')
.filter(l => {
return !ended && (l.length > 0) && (found
? !(ended = l.startsWith('## '))
: (found = l.startsWith(`## ${version}`)));
});
resolve(lines);
} catch (e) {
reject(e);
}
})
.catch(error => {
reject(error);
});
});
};
export const getIPCRenderer = () => {
return ipcRenderer;
};
export const getNewReleases = (existingLocations, newLocations, selectedVersion) => {
const ret = [];
if (existingLocations && newLocations) {
Constants.RELEASE_TYPES.forEach(release => {
newLocations[release]
.filter(version => (version !== selectedVersion) && !existingLocations[release].includes(version) && (version !== 'unavailable'))
.forEach(version => {
ret.splice(0, 0, {
Display: version,
Release: Constants.RELEASE_TYPES.indexOf(release),
Version: newLocations[release].indexOf(version),
VersionString: version,
});
});
});
}
return ret;
};
export const getSelectedVersionFromState = state => {
return (state.relver.Version === -1) ?
'unavailable' :
state.relver.VersionLookup[Constants.RELEASE_TYPES[state.relver.Release]][state.relver.Version];
};

134
src/utils.jsx Normal file
View File

@@ -0,0 +1,134 @@
import axios from 'axios';
import React from 'react';
import Modal from './components/UI/Modal/Modal';
import * as Constants from './constants';
const ipcRenderer =
!process.versions.hasOwnProperty('electron') && window && window.require
? window.require('electron').ipcRenderer
: null;
export const checkNewReleases = (selectedVersion) => {
let previousReleases = localStorage.getItem('previous_releases');
if (previousReleases) {
previousReleases = JSON.parse(previousReleases).VersionLookup;
let currentReleases = localStorage.getItem('releases');
if (currentReleases) {
currentReleases = JSON.parse(currentReleases).VersionLookup;
return getNewReleases(previousReleases, currentReleases, selectedVersion);
}
}
return [];
};
export const createModalConditionally = (
condition,
jsx,
critical,
disableFocusTrap,
transparent
) => {
const modalProps = {
critical: critical,
disableFocusTrap: disableFocusTrap,
transparent: transparent,
};
return condition ? <Modal {...modalProps}>{jsx}</Modal> : null;
};
export const extractFileNameFromURL = (url) => {
const parts = url.split('/');
return parts[parts.length - 1];
};
export const formatLinesForDisplay = (lines) => {
let msg = '';
for (let i = 1; i < lines.length; i++) {
if (i > 1) {
msg += '\n';
}
msg += lines[i].replace(/(\\#)/gm, '#') + '\n';
}
return msg;
};
export const getChangesForRepertoryVersion = (version) => {
return new Promise((resolve, reject) => {
const url =
'https://bitbucket.org/blockstorage/repertory/raw/' +
Constants.REPERTORY_BRANCH +
'/CHANGELOG.md';
axios
.get(url, {
responseType: 'text',
})
.then((response) => {
try {
let found = false;
let ended = false;
let lines = response.data
.replace(/(\r\n)/gm, '\n')
.split('\n')
.filter((l) => {
return (
!ended &&
l.length > 0 &&
(found
? !(ended = l.startsWith('## '))
: (found = l.startsWith(`## ${version}`)))
);
});
resolve(lines);
} catch (e) {
reject(e);
}
})
.catch((error) => {
reject(error);
});
});
};
export const getIPCRenderer = () => {
return ipcRenderer;
};
export const getNewReleases = (
existingLocations,
newLocations,
selectedVersion
) => {
const ret = [];
if (existingLocations && newLocations) {
Constants.RELEASE_TYPES.forEach((release) => {
newLocations[release]
.filter(
(version) =>
version !== selectedVersion &&
!existingLocations[release].includes(version) &&
version !== 'unavailable'
)
.forEach((version) => {
ret.splice(0, 0, {
Display: version,
Release: Constants.RELEASE_TYPES.indexOf(release),
Version: newLocations[release].indexOf(version),
VersionString: version,
});
});
});
}
return ret;
};
export const getSelectedVersionFromState = (state) => {
return state.relver.Version === -1
? 'unavailable'
: state.relver.VersionLookup[Constants.RELEASE_TYPES[state.relver.Release]][
state.relver.Version
];
};