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": [ "cSpell.words": [
"APPIMAGE",
"APPPLATFORM",
"Filebase",
"HKCC",
"HKCR",
"HKCU",
"HKEY", "HKEY",
"HKLM", "HKLM",
"LOCALAPPDATA",
"Redistributable", "Redistributable",
"Skylinks", "Skylinks",
"Skynet", "Skynet",
"Unmount", "Unmount",
"WINFSP",
"blockstorage",
"centos",
"msiexec", "msiexec",
"norestart",
"randomstring",
"reduxjs",
"relver", "relver",
"scprime",
"siaprime", "siaprime",
"skylink" "skylink",
"undocked",
"windir"
] ]
} }

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta
<meta name="theme-color" content="#000000"> 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 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/ 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="manifest" href="%PUBLIC_URL%/manifest.json"/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"/>
<link href="https://fonts.googleapis.com/css?family=Nunito:400,700" rel="stylesheet"> <link
href="https://fonts.googleapis.com/css?family=Nunito:400,700"
rel="stylesheet"
/>
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
@@ -24,9 +30,7 @@
<title>Repertory UI</title> <title>Repertory UI</title>
</head> </head>
<body> <body>
<noscript> <noscript> You need to enable JavaScript to run this app.</noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div> <div id="root"></div>
<!-- <!--
This HTML file is a template. 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 IPCContainer from './containers/IPCContainer/IPCContainer';
import Loading from './components/UI/Loading/Loading'; import Loading from './components/UI/Loading/Loading';
import MountItems from './containers/MountItems/MountItems'; 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 { notifyError } from './redux/actions/error_actions';
import Reboot from './components/Reboot/Reboot'; import Reboot from './components/Reboot/Reboot';
import { import {
setDismissNewReleasesAvailable, setDismissNewReleasesAvailable,
setNewReleasesAvailable setNewReleasesAvailable,
} from './redux/actions/release_version_actions'; } from './redux/actions/release_version_actions';
import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay'; import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay';
import { import {
displaySelectAppPlatform, displaySelectAppPlatform,
saveState saveState,
} from './redux/actions/common_actions'; } from './redux/actions/common_actions';
import SelectAppPlatform from './containers/SelectAppPlatform/SelectAppPlatform'; import SelectAppPlatform from './containers/SelectAppPlatform/SelectAppPlatform';
import Text from './components/UI/Text/Text'; import Text from './components/UI/Text/Text';
@@ -29,10 +29,10 @@ import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
import UpgradeUI from './components/UpgradeUI/UpgradeUI'; import UpgradeUI from './components/UpgradeUI/UpgradeUI';
import { import {
loadReleases, loadReleases,
setDismissUIUpgrade setDismissUIUpgrade,
} from './redux/actions/release_version_actions'; } from './redux/actions/release_version_actions';
import YesNo from './components/YesNo/YesNo'; import YesNo from './components/YesNo/YesNo';
import {createModalConditionally} from './utils'; import { createModalConditionally } from './utils.jsx';
import SkynetImport from './containers/SkynetImport/SkynetImport'; import SkynetImport from './containers/SkynetImport/SkynetImport';
import ApplicationBusy from './components/ApplicationBusy/ApplicationBusy'; import ApplicationBusy from './components/ApplicationBusy/ApplicationBusy';
import SkynetExport from './containers/SkynetExport/SkynetExport'; import SkynetExport from './containers/SkynetExport/SkynetExport';
@@ -59,13 +59,17 @@ class App extends IPCContainer {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if ((prevProps.Release !== this.props.Release) || if (
(prevProps.ReleaseVersion !== this.props.ReleaseVersion) || prevProps.Release !== this.props.Release ||
(prevProps.VersionLookup !== this.props.VersionLookup)) { prevProps.ReleaseVersion !== this.props.ReleaseVersion ||
prevProps.VersionLookup !== this.props.VersionLookup
) {
this.props.saveState(); 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]; return this.props.ProviderState[k] !== prevProps.ProviderState[k];
}).length > 0) { }).length > 0
) {
this.props.saveState(); this.props.saveState();
} }
} }
@@ -76,14 +80,16 @@ class App extends IPCContainer {
} }
getSelectedVersion = () => { getSelectedVersion = () => {
return (this.props.ReleaseVersion === -1) ? return this.props.ReleaseVersion === -1
'unavailable' : ? 'unavailable'
this.props.VersionLookup[Constants.RELEASE_TYPES[this.props.Release]][this.props.ReleaseVersion]; : this.props.VersionLookup[Constants.RELEASE_TYPES[this.props.Release]][
this.props.ReleaseVersion
];
}; };
handleUpgradeIconClicked = () => { handleUpgradeIconClicked = () => {
if (this.props.UpgradeAvailable) { if (this.props.UpgradeAvailable) {
this.props.setDismissUIUpgrade(false) this.props.setDismissUIUpgrade(false);
} else if (this.props.NewReleasesAvailable2.length > 0) { } else if (this.props.NewReleasesAvailable2.length > 0) {
this.props.setNewReleasesAvailable(this.props.NewReleasesAvailable2); this.props.setNewReleasesAvailable(this.props.NewReleasesAvailable2);
this.props.setDismissNewReleasesAvailable(false); this.props.setDismissNewReleasesAvailable(false);
@@ -93,44 +99,54 @@ class App extends IPCContainer {
render() { render() {
const selectedVersion = this.getSelectedVersion(); const selectedVersion = this.getSelectedVersion();
const downloadEnabled = this.props.AllowDownload && const downloadEnabled =
this.props.AllowDownload &&
!this.props.MountsBusy && !this.props.MountsBusy &&
!this.props.DownloadActive && !this.props.DownloadActive &&
(selectedVersion !== 'unavailable') && selectedVersion !== 'unavailable' &&
(selectedVersion !== this.props.InstalledVersion); selectedVersion !== this.props.InstalledVersion;
const missingDependencies = (this.props.MissingDependencies.length > 0) && const missingDependencies =
this.props.AllowMount; this.props.MissingDependencies.length > 0 && this.props.AllowMount;
const allowMount = this.props.AllowMount && const allowMount =
this.props.AllowMount &&
this.props.InstalledVersion !== 'none' && this.props.InstalledVersion !== 'none' &&
!missingDependencies && !missingDependencies &&
!this.props.InstallActive; !this.props.InstallActive;
const remoteSupported = this.props.LocationsLookup[selectedVersion] && const remoteSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].supports_remote; this.props.LocationsLookup[selectedVersion].supports_remote;
const s3Supported = this.props.LocationsLookup[selectedVersion] && const s3Supported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].s3_support; this.props.LocationsLookup[selectedVersion].s3_support;
const skynetSupported = this.props.LocationsLookup[selectedVersion] && const skynetSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].skynet_support; this.props.LocationsLookup[selectedVersion].skynet_support;
const scPrimeSupported = this.props.LocationsLookup[selectedVersion] && const scPrimeSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].siaprime_support; this.props.LocationsLookup[selectedVersion].siaprime_support;
const siaSupported = this.props.LocationsLookup[selectedVersion] && const siaSupported =
this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].sia_support; this.props.LocationsLookup[selectedVersion].sia_support;
const showConfig = !missingDependencies && const showConfig =
!missingDependencies &&
!this.props.DisplayPinnedManager && !this.props.DisplayPinnedManager &&
this.props.DisplayConfiguration && this.props.DisplayConfiguration &&
!this.props.RebootRequired; !this.props.RebootRequired;
const showPinnedManager = !missingDependencies && const showPinnedManager =
!missingDependencies &&
!this.props.RebootRequired && !this.props.RebootRequired &&
this.props.DisplayPinnedManager; this.props.DisplayPinnedManager;
const showUpgrade = this.props.UpgradeAvailable && const showUpgrade =
this.props.UpgradeAvailable &&
!this.props.DisplayError && !this.props.DisplayError &&
!showConfig && !showConfig &&
!this.props.DownloadActive && !this.props.DownloadActive &&
@@ -138,14 +154,16 @@ class App extends IPCContainer {
!this.props.InstallActive && !this.props.InstallActive &&
!this.props.RebootRequired; !this.props.RebootRequired;
const showDependencies = !showUpgrade && const showDependencies =
!showUpgrade &&
missingDependencies && missingDependencies &&
!this.props.DownloadActive && !this.props.DownloadActive &&
!this.props.RebootRequired && !this.props.RebootRequired &&
!this.props.DismissDependencies && !this.props.DismissDependencies &&
this.props.AllowMount; this.props.AllowMount;
const showNewReleases = !showConfig && const showNewReleases =
!showConfig &&
!this.props.DisplayConfirmYesNo && !this.props.DisplayConfirmYesNo &&
!showDependencies && !showDependencies &&
!this.props.DownloadActive && !this.props.DownloadActive &&
@@ -156,9 +174,10 @@ class App extends IPCContainer {
!this.props.DisplaySelectAppPlatform && !this.props.DisplaySelectAppPlatform &&
!showUpgrade && !showUpgrade &&
!this.props.DismissNewReleasesAvailable && !this.props.DismissNewReleasesAvailable &&
(this.props.NewReleasesAvailable.length > 0); this.props.NewReleasesAvailable.length > 0;
const showSkynetImport = !showConfig && const showSkynetImport =
!showConfig &&
!showDependencies && !showDependencies &&
!this.props.DownloadActive && !this.props.DownloadActive &&
!showNewReleases && !showNewReleases &&
@@ -167,7 +186,8 @@ class App extends IPCContainer {
!showUpgrade && !showUpgrade &&
this.props.DisplayImport; this.props.DisplayImport;
const showSkynetExport = !showConfig && const showSkynetExport =
!showConfig &&
!showDependencies && !showDependencies &&
!this.props.DownloadActive && !this.props.DownloadActive &&
!showNewReleases && !showNewReleases &&
@@ -176,66 +196,123 @@ class App extends IPCContainer {
!showUpgrade && !showUpgrade &&
this.props.DisplayExport; this.props.DisplayExport;
const configDisplay = createModalConditionally(showConfig, <Configuration const configDisplay = createModalConditionally(
showConfig,
<Configuration
version={selectedVersion} version={selectedVersion}
s3Supported={s3Supported} s3Supported={s3Supported}
remoteSupported={remoteSupported}/>); remoteSupported={remoteSupported}
const pinnedManagerDisplay = createModalConditionally(showPinnedManager, <PinnedManager />
version={selectedVersion}/>) );
const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo/>); const pinnedManagerDisplay = createModalConditionally(
const dependencyDisplay = createModalConditionally(showDependencies, showPinnedManager,
<DependencyList/>, false, this.props.InstallActive); <PinnedManager version={selectedVersion} />
const downloadDisplay = createModalConditionally(this.props.DownloadActive, );
<DownloadProgress/>, false, true); const confirmDisplay = createModalConditionally(
const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true); this.props.DisplayConfirmYesNo,
const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true); <YesNo />
const newReleasesDisplay = createModalConditionally(showNewReleases, <NewReleases/>); );
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot/>); const dependencyDisplay = createModalConditionally(
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, showDependencies,
<SelectAppPlatform/>); <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 upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI />);
const importDisplay = createModalConditionally(showSkynetImport, <SkynetImport const importDisplay = createModalConditionally(
version={selectedVersion}/>); showSkynetImport,
const exportDisplay = createModalConditionally(showSkynetExport, <SkynetExport <SkynetImport version={selectedVersion} />
version={selectedVersion}/>) );
const appBusyDisplay = createModalConditionally(this.props.AppBusy, const exportDisplay = createModalConditionally(
<ApplicationBusy/>, false, true, this.props.AppBusyTransparent); showSkynetExport,
<SkynetExport version={selectedVersion} />
);
const appBusyDisplay = createModalConditionally(
this.props.AppBusy,
<ApplicationBusy />,
false,
true,
this.props.AppBusyTransparent
);
let mainContent = []; let mainContent = [];
if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) { if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
mainContent = ( mainContent = (
<Box dxStyle={{ height: '100%' }}> <Box dxStyle={{ height: '100%' }}>
<Loading /> <Loading />
</Box>); </Box>
);
} else { } else {
let key = 0; let key = 0;
mainContent.push(( mainContent.push(
<Box key={'md_' + key++} <Box
dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}> key={'md_' + key++}
<ReleaseVersionDisplay downloadDisabled={!downloadEnabled} dxStyle={{ padding: 'var(--default_spacing)', height: 'auto' }}
version={selectedVersion}/> >
<ReleaseVersionDisplay
downloadDisabled={!downloadEnabled}
version={selectedVersion}
/>
</Box> </Box>
)); );
mainContent.push(<div key={'md_' + key++} mainContent.push(
style={{paddingTop: 'var(--default_spacing)'}}/>); <div
key={'md_' + key++}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
if (allowMount) { if (allowMount) {
mainContent.push(( mainContent.push(
<Box dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}} <Box
key={'md_' + key++}> dxStyle={{ padding: 'var(--default_spacing)', height: 'auto' }}
<MountItems s3Supported={s3Supported} key={'md_' + key++}
>
<MountItems
s3Supported={s3Supported}
remoteSupported={remoteSupported} remoteSupported={remoteSupported}
scPrimeSupported={scPrimeSupported} scPrimeSupported={scPrimeSupported}
siaSupported={siaSupported} siaSupported={siaSupported}
skynetSupported={skynetSupported}/> skynetSupported={skynetSupported}
/>
</Box> </Box>
)); );
} else if (!downloadEnabled && (selectedVersion !== 'unavailable')) { } else if (!downloadEnabled && selectedVersion !== 'unavailable') {
mainContent.push(( mainContent.push(
<Box dxStyle={{padding: 'var(--default_spacing)', height: '170px'}} <Box
key={'md_' + key++}> dxStyle={{ padding: 'var(--default_spacing)', height: '170px' }}
key={'md_' + key++}
>
<Loading /> <Loading />
</Box> </Box>
)); );
} }
} }
@@ -245,27 +322,36 @@ class App extends IPCContainer {
<div className={'AppHeader'}> <div className={'AppHeader'}>
<Box> <Box>
<Grid> <Grid>
<Text col={0} <Text
col={0}
colSpan={'remain'} colSpan={'remain'}
row={0} row={0}
rowSpan={'remain'} rowSpan={'remain'}
text={'Repertory UI v' + this.props.Version} text={'Repertory UI v' + this.props.Version}
textAlign={'center'} textAlign={'center'}
type={'Heading1'}/> type={'Heading1'}
/>
<UpgradeIcon <UpgradeIcon
available={!missingDependencies && (this.props.UpgradeAvailable || (this.props.NewReleasesAvailable2.length > 0))} available={
newReleases={!missingDependencies && (!this.props.UpgradeAvailable && (this.props.NewReleasesAvailable2.length > 0))} !missingDependencies &&
(this.props.UpgradeAvailable ||
this.props.NewReleasesAvailable2.length > 0)
}
newReleases={
!missingDependencies &&
!this.props.UpgradeAvailable &&
this.props.NewReleasesAvailable2.length > 0
}
clicked={this.handleUpgradeIconClicked} clicked={this.handleUpgradeIconClicked}
col={dimensions => dimensions.columns - 6} col={(dimensions) => dimensions.columns - 6}
colSpan={5} colSpan={5}
row={1} row={1}
rowSpan={remain => remain - 1}/> rowSpan={(remain) => remain - 1}
/>
</Grid> </Grid>
</Box> </Box>
</div> </div>
<div className={'AppContent'}> <div className={'AppContent'}>{mainContent}</div>
{mainContent}
</div>
</div> </div>
{importDisplay} {importDisplay}
{exportDisplay} {exportDisplay}
@@ -286,7 +372,7 @@ class App extends IPCContainer {
} }
} }
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { return {
AllowDownload: state.download.AllowDownload, AllowDownload: state.download.AllowDownload,
AllowMount: state.common.AllowMount, AllowMount: state.common.AllowMount,
@@ -324,15 +410,19 @@ const mapStateToProps = state => {
}; };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
displaySelectAppPlatform: display => dispatch(displaySelectAppPlatform(display)), displaySelectAppPlatform: (display) =>
dispatch(displaySelectAppPlatform(display)),
loadReleases: () => dispatch(loadReleases()), loadReleases: () => dispatch(loadReleases()),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), notifyError: (msg, critical, callback) =>
dispatch(notifyError(msg, critical, callback)),
saveState: () => dispatch(saveState()), saveState: () => dispatch(saveState()),
setDismissNewReleasesAvailable: dismiss => dispatch(setDismissNewReleasesAvailable(dismiss)), setDismissNewReleasesAvailable: (dismiss) =>
setNewReleasesAvailable: items => dispatch(setNewReleasesAvailable(items)), dispatch(setDismissNewReleasesAvailable(dismiss)),
setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)), setNewReleasesAvailable: (items) =>
dispatch(setNewReleasesAvailable(items)),
setDismissUIUpgrade: (dismiss) => dispatch(setDismissUIUpgrade(dismiss)),
}; };
}; };

View File

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

View File

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

View File

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

View File

@@ -5,10 +5,10 @@ import * as Constants from '../../constants';
import Dependency from './Dependency/Dependency'; import Dependency from './Dependency/Dependency';
import Box from '../UI/Box/Box'; import Box from '../UI/Box/Box';
import { downloadItem } from '../../redux/actions/download_actions'; import { downloadItem } from '../../redux/actions/download_actions';
import {extractFileNameFromURL} from '../../utils'; import { extractFileNameFromURL } from '../../utils.jsx';
import { setDismissDependencies } from '../../redux/actions/install_actions'; import { setDismissDependencies } from '../../redux/actions/install_actions';
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { return {
AllowDismissDependencies: state.relver.AllowDismissDependencies, AllowDismissDependencies: state.relver.AllowDismissDependencies,
MissingDependencies: state.install.MissingDependencies, MissingDependencies: state.install.MissingDependencies,
@@ -17,33 +17,80 @@ const mapStateToProps = state => {
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return { return {
downloadItem: (name, type, url, isWinFSP) => dispatch(downloadItem(name, type, url, isWinFSP)), downloadItem: (name, type, url, isWinFSP) =>
setDismissDependencies: dismiss => dispatch(setDismissDependencies(dismiss)), 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) => { const items = props.MissingDependencies.map((k, i) => {
return ( return (
<Dependency key={i} <Dependency
key={i}
name={k.display} 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 = ( const dismissDisplay = (
<div style={{float: 'right', margin: 0, paddingRight: '4px', boxSizing: 'border-box', display: 'block'}}> <div
<a href={'#'} style={{
onClick={props.AllowDismissDependencies ? () => props.setDismissDependencies(true) : e => e.preventDefault()} float: 'right',
style={{cursor: props.AllowDismissDependencies ? 'pointer' : 'no-drop'}}>X</a> 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> </div>
); );
return ( return (
<Box dxStyle={{ width: '300px', height: 'auto', padding: '5px' }}> <Box dxStyle={{ width: '300px', height: 'auto', padding: '5px' }}>
{dismissDisplay} {dismissDisplay}
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}> <div
<h1 style={{width: '100%', textAlign: 'center', color: 'var(--text_color_error)'}}>Missing Dependencies</h1> 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> </div>
{items} {items}
</Box> </Box>

View File

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

View File

@@ -5,19 +5,23 @@ import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button'; import Button from '../UI/Button/Button';
import './ErrorDetails.css'; import './ErrorDetails.css';
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { 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 { return {
dismissError: () => dispatch(dismissError()), dismissError: () => dispatch(dismissError()),
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(props => { export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
return ( return (
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'ErrorDetailsHeading'}>Application Error</h1> <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 Button from '../UI/Button/Button';
import './InfoDetails.css'; import './InfoDetails.css';
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { 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 { return {
dismissInfo: () => dispatch(dismissInfo()), dismissInfo: () => dispatch(dismissInfo()),
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(props => { export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
let msg = props.InfoMessage.message; let msg = props.InfoMessage.message;
const classes = ['InfoDetailsContent']; const classes = ['InfoDetailsContent'];
@@ -37,7 +41,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
} }
} }
const scrollToTop = textArea => { const scrollToTop = (textArea) => {
if (!textArea.firstClick) { if (!textArea.firstClick) {
textArea.firstClick = true; textArea.firstClick = true;
textArea.scrollTop = 0; textArea.scrollTop = 0;
@@ -49,17 +53,17 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1> <h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1>
<div className={classes.join(' ')}> <div className={classes.join(' ')}>
{ {copyable ? (
copyable ? ( <textarea
<textarea autoFocus autoFocus
rows={9} rows={9}
value={msg} value={msg}
className={'SkynetImportTextArea'} className={'SkynetImportTextArea'}
onClick={e => scrollToTop(e.target)}/> onClick={(e) => scrollToTop(e.target)}
/>
) : ( ) : (
<p style={{ textAlign: 'left' }}>{msg}</p> <p style={{ textAlign: 'left' }}>{msg}</p>
) )}
}
</div> </div>
<Button clicked={props.dismissInfo}>Dismiss</Button> <Button clicked={props.dismissInfo}>Dismiss</Button>
</Box> </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 Button from '../UI/Button/Button';
import { rebootSystem } from '../../redux/actions/common_actions'; import { rebootSystem } from '../../redux/actions/common_actions';
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
rebootSystem: () => dispatch(rebootSystem()), rebootSystem: () => dispatch(rebootSystem()),
}; };
}; };
export default connect(null, mapDispatchToProps)(props => { export default connect(
null,
mapDispatchToProps
)((props) => {
return ( return (
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'RebootHeading'}>Reboot System</h1> <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 { setActiveRelease } from '../../redux/actions/release_version_actions';
import { downloadItem } from '../../redux/actions/download_actions'; import { downloadItem } from '../../redux/actions/download_actions';
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { return {
AllowMount: state.common.AllowMount, AllowMount: state.common.AllowMount,
AppPlatform: state.common.AppPlatform, AppPlatform: state.common.AppPlatform,
@@ -28,128 +28,170 @@ const mapStateToProps = state => {
}; };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
downloadItem: (name, type, urls) => dispatch(downloadItem(name, type, urls)), downloadItem: (name, type, urls) =>
setActiveRelease: (release, version) => dispatch(setActiveRelease(release, version)), 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 = () => { const getSelectedVersion = () => {
return (props.ReleaseVersion === -1) ? return props.ReleaseVersion === -1
'unavailable' : ? 'unavailable'
props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion]; : props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][
props.ReleaseVersion
];
}; };
const handleDownloadRelease = () => { const handleDownloadRelease = () => {
const fileName = props.version + '.zip'; 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 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); props.setActiveRelease(release, releaseVersion);
}; };
const handleVersionChanged = e => { const handleVersionChanged = (e) => {
const releaseVersion = props.VersionLookup[Constants.RELEASE_TYPES[props.Release]].indexOf(e.target.value); const releaseVersion = props.VersionLookup[
Constants.RELEASE_TYPES[props.Release]
].indexOf(e.target.value);
props.setActiveRelease(props.Release, releaseVersion); props.setActiveRelease(props.Release, releaseVersion);
}; };
const text = props.InstalledVersion + ' [' + props.AppPlatform + ']'; const text = props.InstalledVersion + ' [' + props.AppPlatform + ']';
const disabled = props.DownloadActive || const disabled =
props.DownloadActive ||
props.InstallActive || props.InstallActive ||
props.MountsBusy || props.MountsBusy ||
(!props.AllowMount && (getSelectedVersion() !== 'unavailable')) ; (!props.AllowMount && getSelectedVersion() !== 'unavailable');
const releaseExtracting = (props.InstallType === Constants.INSTALL_TYPES.Release); const releaseExtracting =
props.InstallType === Constants.INSTALL_TYPES.Release;
let optionsDisplay = []; let optionsDisplay = [];
let key = 0; let key = 0;
if (releaseExtracting) { if (releaseExtracting) {
optionsDisplay.push(( optionsDisplay.push(
<Text col={dimensions => (dimensions.columns / 3) * 2} <Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'} colSpan={'remain'}
key={key++} key={key++}
rowSpan={4} rowSpan={4}
text={'Activating'} text={'Activating'}
textAlign={'left'} 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'} colSpan={'remain'}
key={key++} key={key++}
row={5} row={5}
rowSpan={7} rowSpan={7}
text={text} text={text}
textAlign={'left'}/> textAlign={'left'}
)); />
);
} else if (props.downloadDisabled || props.DismissDependencies) { } else if (props.downloadDisabled || props.DismissDependencies) {
optionsDisplay.push(( optionsDisplay.push(
<Text col={dimensions => (dimensions.columns / 3) * 2} <Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'} colSpan={'remain'}
key={key++} key={key++}
rowSpan={4} rowSpan={4}
text={'Installed'} text={'Installed'}
textAlign={'left'} textAlign={'left'}
type={'Heading2'}/> type={'Heading2'}
)); />
);
optionsDisplay.push(( optionsDisplay.push(
<Text col={dimensions => (dimensions.columns / 3) * 2} <Text
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={'remain'} colSpan={'remain'}
key={key++} key={key++}
row={5} row={5}
rowSpan={7} rowSpan={7}
text={text} text={text}
textAlign={'left'}/> textAlign={'left'}
)); />
);
} else { } else {
optionsDisplay.push(( optionsDisplay.push(
<Button clicked={handleDownloadRelease} <Button
col={dimensions => (dimensions.columns / 3) * 2} clicked={handleDownloadRelease}
col={(dimensions) => (dimensions.columns / 3) * 2}
colSpan={20} colSpan={20}
key={key++} key={key++}
row={5} row={5}
rowSpan={7}>Install</Button> rowSpan={7}
)); >
Install
</Button>
);
} }
return ( return (
<Grid noScroll> <Grid noScroll>
<Text colSpan={columns=>columns / 3} <Text
colSpan={(columns) => columns / 3}
rowSpan={4} rowSpan={4}
text={'Release'} text={'Release'}
textAlign={'left'} textAlign={'left'}
type={'Heading2'}/> type={'Heading2'}
<DropDown changed={handleReleaseChanged} />
colSpan={remain=>remain / 3 - 1} <DropDown
changed={handleReleaseChanged}
colSpan={(remain) => remain / 3 - 1}
disabled={disabled} disabled={disabled}
items={Constants.RELEASE_TYPES} items={Constants.RELEASE_TYPES}
row={5} row={5}
rowSpan={7} rowSpan={7}
selected={Constants.RELEASE_TYPES[props.Release]}/> selected={Constants.RELEASE_TYPES[props.Release]}
<Text col={dimensions => dimensions.columns / 3} />
colSpan={remain=>remain / 2} <Text
col={(dimensions) => dimensions.columns / 3}
colSpan={(remain) => remain / 2}
rowSpan={4} rowSpan={4}
text={'Version'} text={'Version'}
textAlign={'left'} textAlign={'left'}
type={'Heading2'}/> type={'Heading2'}
<UpgradeIcon available={props.ReleaseUpgradeAvailable} />
col={dimensions => ((dimensions.columns / 3) * 2) - 6} <UpgradeIcon
available={props.ReleaseUpgradeAvailable}
col={(dimensions) => (dimensions.columns / 3) * 2 - 6}
colSpan={4} colSpan={4}
release release
rowSpan={4}/> rowSpan={4}
<DropDown changed={handleVersionChanged} />
col={dimensions => dimensions.columns / 3} <DropDown
colSpan={remain=>remain / 2 - 1} changed={handleVersionChanged}
col={(dimensions) => dimensions.columns / 3}
colSpan={(remain) => remain / 2 - 1}
disabled={disabled} disabled={disabled}
items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]} items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]}
row={5} row={5}
rowSpan={7} rowSpan={7}
selected={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion]}/> selected={
props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][
props.ReleaseVersion
]
}
/>
{optionsDisplay} {optionsDisplay}
</Grid> </Grid>
); );

View File

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

View File

@@ -1,13 +1,17 @@
import React from 'react'; import React from 'react';
import './Button.css'; import './Button.css';
const Button = props => { const Button = (props) => {
return ( return (
<button disabled={props.disabled} <button
disabled={props.disabled}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
className={'Button'} className={'Button'}
style={props.buttonStyles} 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 */ /* Hide the browser's default checkbox */
label.CheckBoxLabel input[type=checkbox] { label.CheckBoxLabel input[type='checkbox'] {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
cursor: pointer; cursor: pointer;
@@ -41,7 +41,7 @@ label.CheckBoxLabel input[type=checkbox] {
} }
/* On mouse-over, add a grey background color */ /* 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); background-color: var(--control_background_hover);
} }
@@ -52,7 +52,7 @@ label.CheckBoxLabel input:checked ~ .CheckBoxCheckMark {
/* Create the CheckBoxCheckMark/indicator (hidden when not checked) */ /* Create the CheckBoxCheckMark/indicator (hidden when not checked) */
.CheckBoxCheckMark:after { .CheckBoxCheckMark:after {
content: ""; content: '';
position: absolute; position: absolute;
display: none; display: none;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ class Configuration extends IPCContainer {
ItemList: [], ItemList: [],
Saving: false, Saving: false,
ShowAdvanced: false, ShowAdvanced: false,
Template: {} Template: {},
}; };
checkItemChanged = (itemA, itemB) => { checkItemChanged = (itemA, itemB) => {
@@ -33,7 +33,7 @@ class Configuration extends IPCContainer {
if (itemA.value.length !== itemB.value.length) { if (itemA.value.length !== itemB.value.length) {
return true; 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; return itemA.value !== itemB.value;
}; };
@@ -52,7 +52,9 @@ class Configuration extends IPCContainer {
const changedObjectItems = []; const changedObjectItems = [];
let j = 0; let j = 0;
for (const item of this.state.ObjectLookup[key]) { 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); changedObjectItems.push(item);
} }
} }
@@ -65,7 +67,7 @@ class Configuration extends IPCContainer {
} }
} }
if ((changedItems.length > 0) || changedObjectLookup) { if (changedItems.length > 0 || changedObjectLookup) {
this.setState({ this.setState({
ChangedItems: changedItems, ChangedItems: changedItems,
ChangedObjectLookup: changedObjectLookup, ChangedObjectLookup: changedObjectLookup,
@@ -77,9 +79,18 @@ class Configuration extends IPCContainer {
componentDidMount() { componentDidMount() {
this._isMounted = true; this._isMounted = true;
this.setRequestHandler(Constants.IPC_Get_Config_Template_Reply, this.onGetConfigTemplateReply); this.setRequestHandler(
this.setRequestHandler(Constants.IPC_Get_Config_Reply, this.onGetConfigReply); Constants.IPC_Get_Config_Template_Reply,
this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply); 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, { this.sendRequest(Constants.IPC_Get_Config_Template, {
Provider: this.props.DisplayConfiguration, Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration, Remote: this.props.DisplayRemoteConfiguration,
@@ -95,25 +106,25 @@ class Configuration extends IPCContainer {
createItemList = (config, template) => { createItemList = (config, template) => {
const objectList = []; const objectList = [];
const itemList = Object const itemList = Object.keys(config)
.keys(config) .map((key) => {
.map(key => {
return { return {
advanced: template[key] ? template[key].advanced : false, advanced: template[key] ? template[key].advanced : false,
hide_remote: template[key] ? template[key].hide_remote : false, hide_remote: template[key] ? template[key].hide_remote : false,
label: key, label: key,
remote: template[key] ? template[key].remote : false, remote: template[key] ? template[key].remote : false,
type: template[key] ? template[key].type : null, type: template[key] ? template[key].type : null,
value: (template[key] && (template[key].type === 'object')) ? value:
config[key] : template[key] && template[key].type === 'object'
(template[key] && (template[key].type === 'string_array')) ? ? config[key]
config[key] : : template[key] && template[key].type === 'string_array'
config[key].toString(), ? config[key]
: config[key].toString(),
}; };
}) })
.filter(i => { .filter((i) => {
let ret = template[i.label]; let ret = template[i.label];
if (ret && (template[i.label].type === 'object')) { if (ret && template[i.label].type === 'object') {
objectList.push(i); objectList.push(i);
ret = false; ret = false;
} }
@@ -122,28 +133,30 @@ class Configuration extends IPCContainer {
return { return {
ObjectList: objectList, ObjectList: objectList,
ItemList: itemList, ItemList: itemList,
} };
}; };
handleItemChanged = (target, idx) => { handleItemChanged = (target, idx) => {
const itemList = [ const itemList = [...this.state.ItemList];
...this.state.ItemList itemList[idx].value =
]; target.type === 'textarea'
itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString(); ? target.string_array
: target.value.toString();
this.setState({ this.setState({
ItemList: itemList ItemList: itemList,
}); });
}; };
handleObjectItemChanged = (target, name, idx) => { handleObjectItemChanged = (target, name, idx) => {
const itemList = [ const itemList = [...this.state.ObjectLookup[name]];
...this.state.ObjectLookup[name]
];
const objectLookup = { const objectLookup = {
...this.state.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; objectLookup[name] = itemList;
this.setState({ this.setState({
ObjectLookup: objectLookup, ObjectLookup: objectLookup,
@@ -157,12 +170,19 @@ class Configuration extends IPCContainer {
let objectLookup = {}; let objectLookup = {};
for (const obj of list.ObjectList) { 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; objectLookup[obj.label] = list2.ItemList;
} }
const isRemoteMount = this.props.remoteSupported && const isRemoteMount =
JSON.parse(objectLookup['RemoteMount'].find(s => s.label === 'IsRemoteMount').value); this.props.remoteSupported &&
JSON.parse(
objectLookup['RemoteMount'].find((s) => s.label === 'IsRemoteMount')
.value
);
if (isRemoteMount) { if (isRemoteMount) {
for (const obj of list.ObjectList) { for (const obj of list.ObjectList) {
if (obj.hide_remote) { if (obj.hide_remote) {
@@ -172,15 +192,16 @@ class Configuration extends IPCContainer {
} }
const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup)); const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
this.setState({ this.setState(
{
IsRemoteMount: isRemoteMount, IsRemoteMount: isRemoteMount,
ItemList: list.ItemList, ItemList: list.ItemList,
ObjectLookup: objectLookup, ObjectLookup: objectLookup,
OriginalItemList: itemListCopy, OriginalItemList: itemListCopy,
OriginalObjectLookup: objectLookupCopy, OriginalObjectLookup: objectLookupCopy,
}, () => { },
() => {}
}); );
} else { } else {
this.props.notifyError(arg.data.Error); this.props.notifyError(arg.data.Error);
} }
@@ -188,16 +209,19 @@ class Configuration extends IPCContainer {
onGetConfigTemplateReply = (event, arg) => { onGetConfigTemplateReply = (event, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
this.setState({ this.setState(
{
Template: arg.data.Template, Template: arg.data.Template,
}, () => { },
() => {
this.sendRequest(Constants.IPC_Get_Config, { this.sendRequest(Constants.IPC_Get_Config, {
Provider: this.props.DisplayConfiguration, Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration, Remote: this.props.DisplayRemoteConfiguration,
S3: this.props.DisplayS3Configuration, S3: this.props.DisplayS3Configuration,
Version: this.props.version, Version: this.props.version,
}); });
}); }
);
} else { } else {
this.props.notifyError(arg.data.Error, false, () => { this.props.notifyError(arg.data.Error, false, () => {
if (this._isMounted) { if (this._isMounted) {
@@ -212,16 +236,17 @@ class Configuration extends IPCContainer {
}; };
saveAndClose = () => { saveAndClose = () => {
this.setState({ this.setState(
{
Saving: true, Saving: true,
}, () => { },
() => {
const changedItems = []; const changedItems = [];
for (const item of this.state.ChangedItems) { for (const item of this.state.ChangedItems) {
changedItems.push({ changedItems.push({
Name: item.label, Name: item.label,
Value: item.type === 'string_array' ? Value:
item.value.join(';') : item.type === 'string_array' ? item.value.join(';') : item.value,
item.value,
}); });
} }
@@ -230,9 +255,10 @@ class Configuration extends IPCContainer {
for (const item of this.state.ChangedObjectLookup[key]) { for (const item of this.state.ChangedObjectLookup[key]) {
changedItems.push({ changedItems.push({
Name: key + '.' + item.label, Name: key + '.' + item.label,
Value: item.type === 'string_array' ? Value:
item.value.join(';') : item.type === 'string_array'
item.value, ? item.value.join(';')
: item.value,
}); });
} }
} }
@@ -245,43 +271,66 @@ class Configuration extends IPCContainer {
S3: this.props.DisplayS3Configuration, S3: this.props.DisplayS3Configuration,
Version: this.props.version, Version: this.props.version,
}); });
}); }
);
}; };
showRemoteConfigItem = (item, itemList) => { showRemoteConfigItem = (item, itemList) => {
if (item.advanced && if (
item.advanced &&
item.remote && item.remote &&
this.props.remoteSupported && this.props.remoteSupported &&
(item.label !== 'IsRemoteMount')) { item.label !== 'IsRemoteMount'
const isRemoteMount = JSON.parse(itemList.find(s => s.label === 'IsRemoteMount').value); ) {
const enableRemoteMount = !isRemoteMount && const isRemoteMount = JSON.parse(
JSON.parse(itemList.find(s => s.label === 'EnableRemoteMount').value); itemList.find((s) => s.label === 'IsRemoteMount').value
return (item.label === 'RemoteHostNameOrIp') || (item.label === 'RemoteMaxConnections') ? );
isRemoteMount : const enableRemoteMount =
(item.label === 'RemoteReceiveTimeoutSeconds') || (item.label === 'RemoteSendTimeoutSeconds') || (item.label === 'RemotePort') || (item.label === 'RemoteToken') ? !isRemoteMount &&
isRemoteMount || enableRemoteMount : JSON.parse(itemList.find((s) => s.label === 'EnableRemoteMount').value);
(item.label === 'EnableRemoteMount') ? return item.label === 'RemoteHostNameOrIp' ||
!isRemoteMount : item.label === 'RemoteMaxConnections'
enableRemoteMount; ? isRemoteMount
: item.label === 'RemoteReceiveTimeoutSeconds' ||
item.label === 'RemoteSendTimeoutSeconds' ||
item.label === 'RemotePort' ||
item.label === 'RemoteToken'
? isRemoteMount || enableRemoteMount
: item.label === 'EnableRemoteMount'
? !isRemoteMount
: enableRemoteMount;
} }
return false; return false;
}; };
render() { render() {
let confirmSave = null; let confirmSave = null;
if ((this.state.ChangedItems.length > 0) || this.state.ChangedObjectLookup) { if (this.state.ChangedItems.length > 0 || this.state.ChangedObjectLookup) {
confirmSave = ( confirmSave = (
<Modal> <Modal>
<Box dxStyle={{ width: '40vw', padding: 'var(--default_spacing)' }}> <Box dxStyle={{ width: '40vw', padding: 'var(--default_spacing)' }}>
<h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1> <h1 style={{ width: '100%', textAlign: 'center' }}>
<table width='100%'> Save Changes?
</h1>
<table width="100%">
<tbody> <tbody>
<tr> <tr>
<td align='center' width='50%'><Button clicked={this.saveAndClose} <td align="center" width="50%">
disabled={this.state.Saving}>Yes</Button> <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>
<td align='center' width='50%'><Button clicked={this.props.hideConfiguration}
disabled={this.state.Saving}>No</Button></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -294,84 +343,105 @@ class Configuration extends IPCContainer {
let objectItems = []; let objectItems = [];
for (const key of Object.keys(this.state.ObjectLookup)) { for (const key of Object.keys(this.state.ObjectLookup)) {
objectItems.push(( objectItems.push(
<div key={key}> <div key={key}>
<h2>{key}</h2> <h2>{key}</h2>
<div> <div>
{ {this.state.ObjectLookup[key].map((k, i) => {
this.state.ObjectLookup[key].map((k, i) => {
const shouldFocus = autoFocus; const shouldFocus = autoFocus;
autoFocus = false; autoFocus = false;
return ( return !k.advanced ||
(!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ? (this.state.ShowAdvanced && k.advanced && !k.remote) ||
<ConfigurationItem advanced={k.advanced} this.showRemoteConfigItem(k, this.state.ObjectLookup[key]) ? (
<ConfigurationItem
advanced={k.advanced}
autoFocus={shouldFocus} autoFocus={shouldFocus}
changed={e => this.handleObjectItemChanged(e, key, i)} changed={(e) => this.handleObjectItemChanged(e, key, i)}
grouping={key} grouping={key}
items={this.state.Template[key].template[k.label].items} items={this.state.Template[key].template[k.label].items}
key={i} key={i}
label={k.label} label={k.label}
readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))} readOnly={
template={this.state.Template[key].template[k.label]} this.state.IsRemoteMount &&
value={k.value}/> : (k.label === 'RemoteHostNameOrIp' ||
null) k.label === 'RemotePort')
})
} }
template={this.state.Template[key].template[k.label]}
value={k.value}
/>
) : null;
})}
</div> </div>
</div> </div>
)); );
} }
const configurationItems = this.state.ItemList const configurationItems = this.state.ItemList.map((k, i) => {
.map((k, i) => {
const shouldFocus = autoFocus; const shouldFocus = autoFocus;
autoFocus = false; autoFocus = false;
return ( return (!this.state.IsRemoteMount || !k.hide_remote) &&
((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ? (!k.advanced || (this.state.ShowAdvanced && k.advanced)) ? (
<ConfigurationItem advanced={k.advanced} <ConfigurationItem
advanced={k.advanced}
autoFocus={shouldFocus} autoFocus={shouldFocus}
changed={e => this.handleItemChanged(e, i)} changed={(e) => this.handleItemChanged(e, i)}
grouping={'Settings'} grouping={'Settings'}
items={this.state.Template[k.label].items} items={this.state.Template[k.label].items}
key={i} key={i}
label={k.label} label={k.label}
template={this.state.Template[k.label]} template={this.state.Template[k.label]}
value={k.value}/> : value={k.value}
null />
); ) : null;
}); });
return ( return (
<div className={'Configuration'}> <div className={'Configuration'}>
{confirmSave} {confirmSave}
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<div style={{ <div
style={{
float: 'right', float: 'right',
margin: 0, margin: 0,
padding: 0, padding: 0,
marginTop: '-4px', marginTop: '-4px',
boxSizing: 'border-box', boxSizing: 'border-box',
display: 'block' display: 'block',
}}> }}
<a href={'#'} >
<a
href={'#'}
onClick={this.checkSaveRequired} onClick={this.checkSaveRequired}
style={{cursor: 'pointer'}}>X</a> style={{ cursor: 'pointer' }}
>
X
</a>
</div> </div>
<h1 style={{width: '100%', textAlign: 'center'}}>{( <h1 style={{ width: '100%', textAlign: 'center' }}>
this.props.DisplayRemoteConfiguration ? {(this.props.DisplayRemoteConfiguration
this.props.DisplayConfiguration.substr(6) : ? this.props.DisplayConfiguration.substr(6)
this.props.DisplayConfiguration) + ' Configuration '} : this.props.DisplayConfiguration) + ' Configuration '}
</h1> </h1>
<div style={{ overflowY: 'auto', height: '90%' }}> <div style={{ overflowY: 'auto', height: '90%' }}>
{this.props.MState.Mounted && (configurationItems.length > 0) ? <Button {this.props.MState.Mounted && configurationItems.length > 0 ? (
buttonStyles={{width: 'auto', height: 'auto', marginLeft: 'auto', marginRight: '4px'}} <Button
buttonStyles={{
width: 'auto',
height: 'auto',
marginLeft: 'auto',
marginRight: '4px',
}}
clicked={() => { clicked={() => {
this.props.displayPinnedManager(true); this.props.displayPinnedManager(true);
return false; return false;
}}>&nbsp;Pinned File Manager...&nbsp;</Button> : null} }}
>
&nbsp;Pinned File Manager...&nbsp;
</Button>
) : null}
<div style={{ marginBottom: '4px' }} /> <div style={{ marginBottom: '4px' }} />
{objectItems} {objectItems}
{(configurationItems.length > 0) ? <h2>Settings</h2> : null} {configurationItems.length > 0 ? <h2>Settings</h2> : null}
{configurationItems} {configurationItems}
</div> </div>
</Box> </Box>
@@ -380,22 +450,23 @@ class Configuration extends IPCContainer {
} }
} }
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { return {
DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration, DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
DisplayS3Configuration: state.mounts.DisplayS3Configuration, DisplayS3Configuration: state.mounts.DisplayS3Configuration,
MState: state.mounts.MountState[state.mounts.DisplayConfiguration], MState: state.mounts.MountState[state.mounts.DisplayConfiguration],
Platform: state.common.Platform, Platform: state.common.Platform,
} };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
displayPinnedManager: display => dispatch(displayPinnedManager(display)), displayPinnedManager: (display) => dispatch(displayPinnedManager(display)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), notifyError: (msg, critical, callback) =>
dispatch(notifyError(msg, critical, callback)),
hideConfiguration: () => dispatch(displayConfiguration(null, false)), hideConfiguration: () => dispatch(displayConfiguration(null, false)),
} };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(Configuration); 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 { connect } from 'react-redux';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { import { notifyError, notifyInfo } from '../../../redux/actions/error_actions';
notifyError,
notifyInfo
} from '../../../redux/actions/error_actions';
import settings from '../../../assets/settings'; import settings from '../../../assets/settings';
import DropDown from '../../../components/UI/DropDown/DropDown'; import DropDown from '../../../components/UI/DropDown/DropDown';
import Password from '../../../containers/UI/Password/Password'; import Password from '../../../containers/UI/Password/Password';
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
notifyError: msg => dispatch(notifyError(msg)), notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)), notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
} };
}; };
export default connect(null, mapDispatchToProps)(props => { export default connect(
null,
mapDispatchToProps
)((props) => {
const handleChanged = (e) => { const handleChanged = (e) => {
const target = e.target; const target = e.target;
if (target.type === 'checkbox') { if (target.type === 'checkbox') {
target.value = e.target.checked ? 'true' : 'false'; target.value = e.target.checked ? 'true' : 'false';
} else if (target.type === 'textarea') { } 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); props.changed(target);
}; };
@@ -37,135 +39,184 @@ export default connect(null, mapDispatchToProps)(props => {
props.notifyInfo(props.label, description); props.notifyInfo(props.label, description);
}; };
infoDisplay = <a href={'#'} infoDisplay = (
<a
href={'#'}
className={'ConfigurationInfo'} className={'ConfigurationInfo'}
onClick={()=>{displayInfo(); return false;}}><FontAwesomeIcon icon={faInfoCircle}/></a>; onClick={() => {
displayInfo();
return false;
}}
>
<FontAwesomeIcon icon={faInfoCircle} />
</a>
);
} }
let data; let data;
switch (props.template.type) { switch (props.template.type) {
case 'bool': case 'bool':
data = <CheckBox changed={handleChanged} data = (
<CheckBox
changed={handleChanged}
checked={props.value} checked={props.value}
disabled={props.readOnly} disabled={props.readOnly}
autoFocus={props.autoFocus}/>; autoFocus={props.autoFocus}
/>
);
break; break;
case 'double': case 'double':
data = <input min={0.0} data = (
<input
min={0.0}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
disabled={props.readOnly} disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={(e) => handleChanged(e)}
step={'0.01'} step={'0.01'}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
value={parseFloat(props.value).toFixed(2)}/>; value={parseFloat(props.value).toFixed(2)}
/>
);
break; break;
case 'list': case 'list':
data = <DropDown alt data = (
<DropDown
alt
auto auto
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
changed={handleChanged} changed={handleChanged}
disabled={props.readOnly} disabled={props.readOnly}
items={props.items} items={props.items}
selected={props.value} />; selected={props.value}
/>
);
break; break;
case 'string': case 'string':
if (props.template.subtype === 'password') { if (props.template.subtype === 'password') {
data = ( data = (
<Password autoFocus={props.autoFocus} <Password
changed={s => handleChanged({ autoFocus={props.autoFocus}
changed={(s) =>
handleChanged({
target: { target: {
type: 'password', type: 'password',
value: s, value: s,
}, },
})} })
}
disabled={props.readOnly} disabled={props.readOnly}
mismatchHandler={() => props.notifyError('Passwords do not match')} mismatchHandler={() => props.notifyError('Passwords do not match')}
value={props.value} /> value={props.value}
/>
); );
} else { } else {
data = ( data = (
<input onChange={e => handleChanged(e)} <input
onChange={(e) => handleChanged(e)}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
disabled={props.readOnly} disabled={props.readOnly}
type={'text'} type={'text'}
value={props.value}/> value={props.value}
/>
); );
} }
break; break;
case 'uint8': case 'uint8':
data = <input max={255} data = (
<input
max={255}
min={0} min={0}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
disabled={props.readOnly} disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
value={props.value}/>; value={props.value}
/>
);
break; break;
case 'uint16': case 'uint16':
data = <input max={65535} data = (
<input
max={65535}
min={0} min={0}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
disabled={props.readOnly} disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
value={props.value}/>; value={props.value}
/>
);
break; break;
case 'uint32': case 'uint32':
data = <input max={4294967295} data = (
<input
max={4294967295}
min={0} min={0}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
disabled={props.readOnly} disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
value={props.value}/>; value={props.value}
/>
);
break; break;
case 'uint64': case 'uint64':
data = <input max={18446744073709551615} data = (
<input
max={18446744073709551615}
min={0} min={0}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
disabled={props.readOnly} disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
value={props.value}/>; value={props.value}
/>
);
break; break;
case 'string_array': case 'string_array':
data = ( data = (
<textarea autoFocus={props.autoFocus} <textarea
autoFocus={props.autoFocus}
disabled={props.readOnly} disabled={props.readOnly}
rows={4} rows={4}
cols={36} cols={36}
onChange={e=>handleChanged(e)} onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
value={props.value.join('\n')} /> value={props.value.join('\n')}
/>
); );
break; break;
case 'password': case 'password':
data = ( data = (
<Password autoFocus={props.autoFocus} <Password
changed={s => handleChanged({ autoFocus={props.autoFocus}
changed={(s) =>
handleChanged({
target: { target: {
type: 'password', type: 'password',
value: s, value: s,
}, },
})} })
}
disabled={props.readOnly} disabled={props.readOnly}
mismatchHandler={() => props.notifyError('Passwords do not match')} mismatchHandler={() => props.notifyError('Passwords do not match')}
value={props.value} /> value={props.value}
/>
); );
break; break;
@@ -175,13 +226,18 @@ export default connect(null, mapDispatchToProps)(props => {
return ( return (
<div className={'ConfigurationItem'}> <div className={'ConfigurationItem'}>
<table cellPadding='2' <table cellPadding="2" width="100%">
width='100%'>
<tbody> <tbody>
<tr> <tr>
{infoDisplay ? {infoDisplay ? (
<td width='100%' valign={'top'}>{infoDisplay} {props.label}</td> : <td width="100%" valign={'top'}>
<td width='100%' valign={'top'}>{props.label}</td>} {infoDisplay} {props.label}
</td>
) : (
<td width="100%" valign={'top'}>
{props.label}
</td>
)}
<td>{data}</td> <td>{data}</td>
</tr> </tr>
</tbody> </tbody>

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button'; import Button from '../../components/UI/Button/Button';
import { import {
downloadItem, downloadItem,
setAllowDownload setAllowDownload,
} from '../../redux/actions/download_actions'; } from '../../redux/actions/download_actions';
import DropDown from '../../components/UI/DropDown/DropDown'; import DropDown from '../../components/UI/DropDown/DropDown';
import IPCContainer from '../IPCContainer/IPCContainer'; import IPCContainer from '../IPCContainer/IPCContainer';
@@ -19,25 +19,32 @@ class SelectAppPlatform extends IPCContainer {
Selected: 0, Selected: 0,
}; };
grabLatestRelease = appPlatform => { grabLatestRelease = (appPlatform) => {
const errorHandler = error => { const errorHandler = (error) => {
this.props.notifyError(error); this.props.notifyError(error);
this.props.setInstallTestActive(false); this.props.setInstallTestActive(false);
this.props.setAllowDownload(false); this.props.setAllowDownload(false);
}; };
axios axios
.get(Constants.RELEASES_URL) .get(Constants.RELEASES_URL)
.then(response => { .then((response) => {
try { try {
const releases = response.data.Versions.Release[appPlatform]; const releases = response.data.Versions.Release[appPlatform];
const latestVersion = releases[releases.length - 1]; const latestVersion = releases[releases.length - 1];
const release = response.data.Locations[appPlatform][latestVersion]; 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) { } catch (error) {
errorHandler(error); errorHandler(error);
} }
}) })
.catch(error => { .catch((error) => {
errorHandler(error); errorHandler(error);
}); });
}; };
@@ -45,10 +52,12 @@ class SelectAppPlatform extends IPCContainer {
handleTestClicked = () => { handleTestClicked = () => {
this.props.setInstallTestActive(true); this.props.setInstallTestActive(true);
this.props.setAllowDownload(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.setState({
...this.state, ...this.state,
Selected: Constants.LINUX_SELECTABLE_PLATFORMS.indexOf(e.target.value), Selected: Constants.LINUX_SELECTABLE_PLATFORMS.indexOf(e.target.value),
@@ -60,33 +69,45 @@ class SelectAppPlatform extends IPCContainer {
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'SAPHeading'}>Select Linux Platform</h1> <h1 className={'SAPHeading'}>Select Linux Platform</h1>
<div className={'SAPContent'}> <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>
<div className={'SAPActions'}> <div className={'SAPActions'}>
<DropDown changed={this.handleChanged} <DropDown
changed={this.handleChanged}
disabled={this.props.InstallTestActive} disabled={this.props.InstallTestActive}
items={Constants.LINUX_SELECTABLE_PLATFORMS} items={Constants.LINUX_SELECTABLE_PLATFORMS}
selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}/> selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}
<Button clicked={this.handleTestClicked} />
disabled={this.props.InstallTestActive}>Test</Button> <Button
clicked={this.handleTestClicked}
disabled={this.props.InstallTestActive}
>
Test
</Button>
</div> </div>
</Box> </Box>
); );
} }
} }
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { return {
InstallTestActive: state.install.InstallTestActive, InstallTestActive: state.install.InstallTestActive,
} };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
downloadItem: (name, type, urls, isWinFSP, testVersion, appPlatform) => dispatch(downloadItem(name, type, urls, isWinFSP, testVersion, appPlatform)), downloadItem: (name, type, urls, isWinFSP, testVersion, appPlatform) =>
notifyError: msg => dispatch(notifyError(msg)), dispatch(
setAllowDownload: allow => dispatch(setAllowDownload(allow)), downloadItem(name, type, urls, isWinFSP, testVersion, appPlatform)
setInstallTestActive: active => dispatch(setInstallTestActive(active)), ),
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 { displaySkynetExport } from '../../redux/actions/skynet_actions';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { import {
faCheckSquare, faChevronDown, faCheckSquare,
faChevronRight, faFile, faFolder, faFolderOpen, faChevronDown,
faHSquare, faMinusSquare, faPlusSquare, faChevronRight,
faSquare faFile,
faFolder,
faFolderOpen,
faHSquare,
faMinusSquare,
faPlusSquare,
faSquare,
} from '@fortawesome/free-solid-svg-icons'; } from '@fortawesome/free-solid-svg-icons';
import Button from '../../components/UI/Button/Button'; import Button from '../../components/UI/Button/Button';
const Constants = require('../../constants'); const Constants = require('../../constants');
const mapStateToProps = state => { const mapStateToProps = (state) => {
return { return {
AppBusy: state.common.AppBusy, AppBusy: state.common.AppBusy,
}; };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = (dispatch) => {
return { return {
displaySkynetExport: display => dispatch(displaySkynetExport(display)), displaySkynetExport: (display) => dispatch(displaySkynetExport(display)),
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)), notifyApplicationBusy: (busy) =>
notifyError: msg => dispatch(notifyError(msg)), dispatch(notifyApplicationBusy(busy, true)),
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, 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 = { state = {
checked: [], checked: [],
clicked: {}, clicked: {},
expanded: [], expanded: [],
nodes: [], nodes: [],
second_stage: false, second_stage: false,
} };
componentDidMount() { componentDidMount() {
this.setRequestHandler(Constants.IPC_Grab_Skynet_Tree_Reply, this.onGrabSkynetTreeReply); this.setRequestHandler(
this.setRequestHandler(Constants.IPC_Export_Skylinks_Reply, this.onExportSkylinksReply); Constants.IPC_Grab_Skynet_Tree_Reply,
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version}); this.onGrabSkynetTreeReply
);
this.setRequestHandler(
Constants.IPC_Export_Skylinks_Reply,
this.onExportSkylinksReply
);
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {
Version: this.props.version,
});
} }
componentWillUnmount() { componentWillUnmount() {
super.componentWillUnmount(); super.componentWillUnmount();
} }
createNodes = items => { createNodes = (items) => {
/* /*
{ {
name: '', name: '',
@@ -66,7 +85,12 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
const treeItem = { const treeItem = {
label: item.name, label: item.name,
path: item.path, 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) { if (item.directory) {
@@ -77,7 +101,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
} }
return ret; return ret;
} };
handleNavigation = () => { handleNavigation = () => {
if (this.state.second_stage) { if (this.state.second_stage) {
@@ -95,25 +119,34 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
}); });
} }
} }
} };
onExportSkylinksReply = (_, arg) => { onExportSkylinksReply = (_, arg) => {
this.props.notifyApplicationBusy(false); this.props.notifyApplicationBusy(false);
if (arg.data.Success) { if (arg.data.Success) {
this.setState({ this.setState(
{
checked: [], checked: [],
clicked: {}, clicked: {},
expanded: [], expanded: [],
nodes: [], nodes: [],
second_stage: false, 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 { } else {
this.props.notifyError(arg.data.Error); this.props.notifyError(arg.data.Error);
} }
} };
onGrabSkynetTreeReply = (_, arg) => { onGrabSkynetTreeReply = (_, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
@@ -123,23 +156,31 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
nodes: this.createNodes(arg.data.Result), nodes: this.createNodes(arg.data.Result),
}); });
} else { } else {
this.setState({ this.setState(
{
checked: [], checked: [],
expanded: [], expanded: [],
nodes: [], nodes: [],
}, () => { },
() => {
this.props.notifyError(arg.data.Error); this.props.notifyError(arg.data.Error);
});
} }
);
} }
};
render() { render() {
return this.props.AppBusy ? (<div/>) : ( return this.props.AppBusy ? (
<Box dxDark dxStyle={{ <div />
) : (
<Box
dxDark
dxStyle={{
height: '90vh', height: '90vh',
padding: 'var(--default_spacing)', padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)' width: 'calc(100vw - (var(--default_spacing) * 4)',
}}> }}
>
<div <div
style={{ style={{
float: 'right', float: 'right',
@@ -147,89 +188,161 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
padding: 0, padding: 0,
marginTop: '-4px', marginTop: '-4px',
boxSizing: 'border-box', boxSizing: 'border-box',
display: 'block' display: 'block',
}}> }}
<a href={'#'} >
<a
href={'#'}
onClick={() => this.props.displaySkynetExport(false)} onClick={() => this.props.displaySkynetExport(false)}
style={{cursor: 'pointer'}}>X</a> style={{ cursor: 'pointer' }}
>
X
</a>
</div> </div>
<h1 <h1 className={'SkynetExportHeading'}>
className={'SkynetExportHeading'}>{this.state.second_stage ? 'Verify Exports' : 'Export Files'}</h1> {this.state.second_stage ? 'Verify Exports' : 'Export Files'}
<div className={this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'}> </h1>
{ <div
this.state.second_stage ? className={
this.state.checked.map(path => { this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'
}
>
{this.state.second_stage ? (
this.state.checked.map((path) => {
return ( return (
<input readOnly <input
readOnly
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
key={path} key={path}
style={{width: '100%', marginBottom: 'var(--default_spacing)'}} style={{
width: '100%',
marginBottom: 'var(--default_spacing)',
}}
type={'text'} type={'text'}
value={path}/> value={path}
/>
); );
}) })
: ( ) : (
<CheckboxTree checked={this.state.checked} <CheckboxTree
checked={this.state.checked}
expanded={this.state.expanded} expanded={this.state.expanded}
expandOnClick expandOnClick
showExpandAll showExpandAll
icons={{ icons={{
check: <FontAwesomeIcon icon={faCheckSquare} fixedWidth check: (
style={{padding: 0, margin: 0}}/>, <FontAwesomeIcon
uncheck: <FontAwesomeIcon icon={faSquare} fixedWidth icon={faCheckSquare}
style={{padding: 0, margin: 0}}/>, fixedWidth
halfCheck: <FontAwesomeIcon icon={faHSquare} fixedWidth style={{ padding: 0, margin: 0 }}
style={{padding: 0, margin: 0}}/>, />
expandClose: <FontAwesomeIcon icon={faChevronRight} fixedWidth ),
style={{padding: 0, margin: 0}}/>, uncheck: (
expandOpen: <FontAwesomeIcon icon={faChevronDown} fixedWidth <FontAwesomeIcon
style={{padding: 0, margin: 0}}/>, icon={faSquare}
expandAll: <FontAwesomeIcon icon={faPlusSquare} fixedWidth fixedWidth
style={{padding: 0, margin: 0}}/>, style={{ padding: 0, margin: 0 }}
collapseAll: <FontAwesomeIcon icon={faMinusSquare} fixedWidth />
style={{padding: 0, margin: 0}}/>, ),
parentClose: <FontAwesomeIcon icon={faFolder} 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 fixedWidth
color={'var(--heading_text_color)'} color={'var(--heading_text_color)'}
style={{padding: 0, margin: 0}}/>, style={{ padding: 0, margin: 0 }}
parentOpen: <FontAwesomeIcon icon={faFolderOpen} />
),
parentOpen: (
<FontAwesomeIcon
icon={faFolderOpen}
fixedWidth fixedWidth
color={'var(--heading_text_color)'} color={'var(--heading_text_color)'}
style={{padding: 0, margin: 0}}/>, style={{ padding: 0, margin: 0 }}
leaf: <FontAwesomeIcon icon={faFile} />
),
leaf: (
<FontAwesomeIcon
icon={faFile}
fixedWidth fixedWidth
color={'var(--text_color)'} color={'var(--text_color)'}
style={{padding: 0, margin: 0}}/> style={{ padding: 0, margin: 0 }}
/>
),
}} }}
nodes={this.state.nodes} nodes={this.state.nodes}
onClick={clicked => this.setState({clicked})} onClick={(clicked) => this.setState({ clicked })}
onCheck={checked => this.setState({checked})} onCheck={(checked) => this.setState({ checked })}
onExpand={expanded => this.setState({expanded})}/> onExpand={(expanded) => this.setState({ expanded })}
) />
} )}
</div> </div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}> <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
{ {this.state.second_stage ? (
this.state.second_stage ? <Button
<Button buttonStyles={{ buttonStyles={{
height: 'auto', height: 'auto',
marginLeft: 'var(--default_spacing)', marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)', marginTop: 'var(--default_spacing)',
width: 'auto' 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 ? '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> </div>
</Box> </Box>
); );
} }
}); }
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,8 @@
import * as Constants from '../../constants';
import { createAction } from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
import {
getIPCRenderer, import * as Constants from '../../constants';
getSelectedVersionFromState import { getIPCRenderer, getSelectedVersionFromState } from '../../utils.jsx';
} from '../../utils';
import {notifyError} from './error_actions';
import {downloadItem, setAllowDownload} from './download_actions';
import {
loadReleases,
setActiveRelease,
setInstalledVersion,
setReleaseUpgradeAvailable,
setNewReleasesAvailable2,
} from './release_version_actions';
import { import {
confirmYesNo, confirmYesNo,
displaySelectAppPlatform, displaySelectAppPlatform,
@@ -21,18 +11,28 @@ import {
setLinuxAppPlatform, setLinuxAppPlatform,
setRebootRequired, setRebootRequired,
showWindow, showWindow,
shutdownApplication shutdownApplication,
} from './common_actions'; } from './common_actions';
import { downloadItem, setAllowDownload } from './download_actions';
import { notifyError } from './error_actions';
import { unmountAll } from './mount_actions'; import { unmountAll } from './mount_actions';
import {
loadReleases,
setActiveRelease,
setInstalledVersion,
setNewReleasesAvailable2,
setReleaseUpgradeAvailable,
} from './release_version_actions';
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
export const checkInstalled = (dependencies, version) => { export const checkInstalled = (dependencies, version) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const checkInstalledComplete = (event, arg) => { const checkInstalledComplete = (_, arg) => {
const result = arg.data; const result = arg.data;
const updateState = () => { const updateState = () => {
const installedVersion = result.Success && result.Exists ? result.Version : 'none'; const installedVersion =
result.Success && result.Exists ? result.Version : 'none';
const state = getState(); const state = getState();
const release = state.relver.Release; const release = state.relver.Release;
@@ -40,7 +40,9 @@ export const checkInstalled = (dependencies, version) => {
let upgradeAvailable = false; let upgradeAvailable = false;
if (installedVersion !== 'none') { 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) { if (version === -1) {
version = latestVersion; version = latestVersion;
dispatch(setActiveRelease(release, version)); dispatch(setActiveRelease(release, version));
@@ -57,14 +59,18 @@ export const checkInstalled = (dependencies, version) => {
const autoInstallRelease = getState().install.AutoInstallRelease; const autoInstallRelease = getState().install.AutoInstallRelease;
dispatch(setAutoInstallRelease(false)); dispatch(setAutoInstallRelease(false));
if (result.Dependencies && (result.Dependencies.length > 0)) { if (result.Dependencies && result.Dependencies.length > 0) {
dispatch(showWindow()); dispatch(showWindow());
} else if ((installedVersion === 'none') && autoInstallRelease) { } else if (installedVersion === 'none' && autoInstallRelease) {
dispatch(setAllowMount(false)); 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 urls = getState().relver.LocationsLookup[versionString].urls;
const fileName = versionString + '.zip'; 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, { ipcRenderer.send(Constants.IPC_Check_Installed, {
Dependencies: dependencies, Dependencies: dependencies,
Version: version, Version: version,
@@ -89,10 +98,14 @@ export const checkVersionInstalled = () => {
dispatch(setAllowDownload(false)); dispatch(setAllowDownload(false));
const selectedVersion = getSelectedVersionFromState(state); const selectedVersion = getSelectedVersionFromState(state);
if (selectedVersion && (selectedVersion !== 'unavailable')) { if (selectedVersion && selectedVersion !== 'unavailable') {
let dependencies = []; let dependencies = [];
if (state.relver.LocationsLookup[selectedVersion] && state.relver.LocationsLookup[selectedVersion].dependencies) { if (
dependencies = state.relver.LocationsLookup[selectedVersion].dependencies; state.relver.LocationsLookup[selectedVersion] &&
state.relver.LocationsLookup[selectedVersion].dependencies
) {
dependencies =
state.relver.LocationsLookup[selectedVersion].dependencies;
} }
dispatch(checkInstalled(dependencies, selectedVersion)); dispatch(checkInstalled(dependencies, selectedVersion));
} else { } else {
@@ -107,7 +120,7 @@ export const installDependency = (source, url, isWinFSP) => {
if (ipcRenderer && !getState().install.InstallActive) { if (ipcRenderer && !getState().install.InstallActive) {
dispatch(setInstallActive(Constants.INSTALL_TYPES.Dependency)); dispatch(setInstallActive(Constants.INSTALL_TYPES.Dependency));
const installDependencyComplete = (event, arg) => { const installDependencyComplete = (_, arg) => {
const result = arg.data; const result = arg.data;
const handleCompleted = () => { const handleCompleted = () => {
if (result.RebootRequired) { if (result.RebootRequired) {
@@ -122,13 +135,16 @@ export const installDependency = (source, url, isWinFSP) => {
}; };
if (result.Success && source.toLowerCase().endsWith('.dmg')) { 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; return d.download === url;
}); });
const i = setInterval(() => { 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, File: dep.file,
}); }
);
if (ret.data.Exists) { if (ret.data.Exists) {
clearInterval(i); 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, { ipcRenderer.send(Constants.IPC_Install_Dependency, {
Source: source, Source: source,
URL: url, URL: url,
@@ -160,7 +179,7 @@ export const installAndTestRelease = (source, version, appPlatform) => {
FilePath: source, FilePath: source,
}); });
ipcRenderer.once(Constants.IPC_Test_Release_Reply, (event, arg) => { ipcRenderer.once(Constants.IPC_Test_Release_Reply, (_, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
ipcRenderer.sendSync(Constants.IPC_Set_Linux_AppPlatform, { ipcRenderer.sendSync(Constants.IPC_Set_Linux_AppPlatform, {
AppPlatform: appPlatform, AppPlatform: appPlatform,
@@ -177,10 +196,13 @@ export const installAndTestRelease = (source, version, appPlatform) => {
}); });
ipcRenderer.send(Constants.IPC_Test_Release, { ipcRenderer.send(Constants.IPC_Test_Release, {
Version: version, Version: version,
}) });
}; };
ipcRenderer.once(Constants.IPC_Extract_Release_Complete, extractReleaseComplete); ipcRenderer.once(
Constants.IPC_Extract_Release_Complete,
extractReleaseComplete
);
ipcRenderer.send(Constants.IPC_Extract_Release, { ipcRenderer.send(Constants.IPC_Extract_Release, {
Source: source, Source: source,
Version: version, Version: version,
@@ -192,7 +214,10 @@ export const installAndTestRelease = (source, version, appPlatform) => {
export const installReleaseByVersion = (release, version) => { export const installReleaseByVersion = (release, version) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const install = () => { const install = () => {
if (getState().download.AllowDownload && !getState().download.DownloadActive) { if (
getState().download.AllowDownload &&
!getState().download.DownloadActive
) {
dispatch(setAutoInstallRelease(true)); dispatch(setAutoInstallRelease(true));
dispatch(setActiveRelease(release, version)); dispatch(setActiveRelease(release, version));
} else { } else {
@@ -202,31 +227,34 @@ export const installReleaseByVersion = (release, version) => {
if (getState().mounts.MountsBusy) { if (getState().mounts.MountsBusy) {
dispatch(confirmYesNo('Unmount all drives?')) dispatch(confirmYesNo('Unmount all drives?'))
.then(confirmed => { .then((confirmed) => {
if (confirmed) { if (confirmed) {
dispatch(unmountAll(install)); dispatch(unmountAll(install));
} }
}) })
.catch(error => notifyError(error)); .catch((error) => notifyError(error));
} else { } else {
install(); install();
} }
}; };
}; };
export const installRelease = source => { export const installRelease = (source) => {
return (dispatch, getState) => { return (dispatch, getState) => {
if (ipcRenderer && !getState().install.InstallActive) { if (ipcRenderer && !getState().install.InstallActive) {
dispatch(setInstallActive(Constants.INSTALL_TYPES.Release)); dispatch(setInstallActive(Constants.INSTALL_TYPES.Release));
const version = getSelectedVersionFromState(getState()); const version = getSelectedVersionFromState(getState());
const extractReleaseComplete = (event, arg) => { const extractReleaseComplete = (_, arg) => {
ipcRenderer.send(Constants.IPC_Delete_File, { ipcRenderer.send(Constants.IPC_Delete_File, {
FilePath: source, FilePath: source,
}); });
if (arg.data.Success) { if (arg.data.Success) {
localStorage.setItem('previous_releases', localStorage.getItem('releases')); localStorage.setItem(
'previous_releases',
localStorage.getItem('releases')
);
dispatch(setNewReleasesAvailable2([])); dispatch(setNewReleasesAvailable2([]));
} }
@@ -234,7 +262,10 @@ export const installRelease = source => {
dispatch(checkVersionInstalled()); dispatch(checkVersionInstalled());
}; };
ipcRenderer.once(Constants.IPC_Extract_Release_Complete, extractReleaseComplete); ipcRenderer.once(
Constants.IPC_Extract_Release_Complete,
extractReleaseComplete
);
ipcRenderer.send(Constants.IPC_Extract_Release, { ipcRenderer.send(Constants.IPC_Extract_Release, {
Source: source, Source: source,
Version: version, Version: version,
@@ -249,23 +280,33 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => {
dispatch(setInstallActive(Constants.INSTALL_TYPES.Upgrade)); dispatch(setInstallActive(Constants.INSTALL_TYPES.Upgrade));
dispatch(setApplicationReady(false)); dispatch(setApplicationReady(false));
const installUpgradeComplete = (event, arg) => { const installUpgradeComplete = (_, arg) => {
const result = arg.data; const result = arg.data;
if (result.Success) { if (result.Success) {
dispatch(shutdownApplication()); dispatch(shutdownApplication());
} else { } else {
dispatch(setApplicationReady(true)); dispatch(setApplicationReady(true));
dispatch(setInstallComplete(result)); dispatch(setInstallComplete(result));
dispatch(notifyError(result.Error, false, () => { dispatch(
notifyError(
result.Error,
false,
() => {
// TODO Prompt to verify // TODO Prompt to verify
if (result.AllowSkipVerification) { if (result.AllowSkipVerification) {
dispatch(installUpgrade(source, sha256, signature, true)); 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, { ipcRenderer.send(Constants.IPC_Install_Upgrade, {
Sha256: sha256, Sha256: sha256,
Signature: signature, Signature: signature,
@@ -276,9 +317,17 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => {
}; };
}; };
export const setAutoInstallRelease = createAction('install/setAutoInstallRelease'); export const setAutoInstallRelease = createAction(
export const setDismissDependencies = createAction('install/setDismissDependencies'); 'install/setAutoInstallRelease'
);
export const setDismissDependencies = createAction(
'install/setDismissDependencies'
);
export const setInstallActive = createAction('install/setInstallActive'); 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 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 {createAction} from '@reduxjs/toolkit';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {getIPCRenderer} from '../../utils'; import {getIPCRenderer} from '../../utils.jsx';
import {confirmYesNo, saveState} from './common_actions'; import {confirmYesNo, saveState} from './common_actions';
import {notifyError} from './error_actions'; import {notifyError} from './error_actions';
@@ -23,8 +23,10 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
Version: getState().relver.InstalledVersion, Version: getState().relver.InstalledVersion,
}); });
} else { } else {
dispatch( dispatch(noti
notifyError('Failed to set \'RemoteToken\': ' + arg.data.Error)); 'Failed to set \'RemoteToken\': '
oken
': " + arg.data.Error));
dispatch(setBusy(false)); 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) => { return (dispatch, getState) => {
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
const provider = 'S3' + name; const provider = 'S3' + name;
@@ -60,7 +69,8 @@ export const addS3Mount = (name, accessKey, secretKey, region, bucketName, url)
}); });
} else { } else {
dispatch( dispatch(
notifyError('Failed to create S3 instance: ' + arg.data.Error)); notifyError('Failed to create S3 instance: ' + arg.data.Error)
);
dispatch(setBusy(false)); dispatch(setBusy(false));
} }
}); });
@@ -95,11 +105,12 @@ export const displayConfiguration = (provider, remote, s3) => {
}; };
}; };
export const removeMount = provider => { export const removeMount = (provider) => {
return dispatch => { return (dispatch) => {
const isRemote = provider.startsWith('Remote'); const isRemote = provider.startsWith('Remote');
dispatch(confirmYesNo('Delete [' + provider.substr(isRemote ? 6 : 2) + ']?')) dispatch(
.then(confirmed => { confirmYesNo('Delete [' + provider.substr(isRemote ? 6 : 2) + ']?')
).then((confirmed) => {
if (confirmed) { if (confirmed) {
dispatch(removeMount2(provider)); dispatch(removeMount2(provider));
} }
@@ -107,8 +118,8 @@ export const removeMount = provider => {
}; };
}; };
const removeMount2 = provider => { const removeMount2 = (provider) => {
return dispatch => { return (dispatch) => {
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
ipcRenderer.once(Constants.IPC_Remove_Mount_Reply, (_, arg) => { ipcRenderer.once(Constants.IPC_Remove_Mount_Reply, (_, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
@@ -119,7 +130,7 @@ const removeMount2 = provider => {
const isRemote = provider.startsWith('Remote'); const isRemote = provider.startsWith('Remote');
ipcRenderer.send(Constants.IPC_Remove_Mount, { ipcRenderer.send(Constants.IPC_Remove_Mount, {
Remote: isRemote, 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 RESET_MOUNTS_STATE = 'mounts/resetMountsState';
export const 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 SET_ALLOW_MOUNT = 'mounts/setAllowMount';
export const setAllowMount = export const setAllowMount = (provider, allow) => {
(provider,
allow) => {
return {type: SET_ALLOW_MOUNT, payload: {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 setBusy = createAction('mounts/setBusy');
export const SET_MOUNT_STATE = 'mounts/setMountState'; export const SET_MOUNT_STATE = 'mounts/setMountState';
export const setMountState = export const setMountState = (provider, state) => {
(provider,
state) => {
return {type: SET_MOUNT_STATE, payload: {provider, state}}; return {type: SET_MOUNT_STATE, payload: {provider, state}};
}; };
export const SET_MOUNTED = 'mounts/setMounted'; export const SET_MOUNTED = 'mounts/setMounted';
export const setMounted = export const setMounted = (provider, mounted) => {
(provider,
mounted) => {
return {type: SET_MOUNTED, payload: {provider, mounted}}; return {type: SET_MOUNTED, payload: {provider, mounted}};
}; };
export const SET_PROVIDER_STATE = 'mounts/setProviderState'; export const SET_PROVIDER_STATE = 'mounts/setProviderState';
export const setProviderState = (provider, state) => { 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 => { export const unmountAll = (completedCallback) => {
return dispatch => { return (dispatch) => {
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
const unmountedCallback = () => { const unmountedCallback = () => {
dispatch(resetMountsState()); dispatch(resetMountsState());
@@ -173,5 +178,5 @@ export const unmountAll = completedCallback => {
}; };
ipcRenderer.once(Constants.IPC_Unmount_All_Drives_Reply, unmountedCallback); ipcRenderer.once(Constants.IPC_Unmount_All_Drives_Reply, unmountedCallback);
ipcRenderer.send(Constants.IPC_Unmount_All_Drives); ipcRenderer.send(Constants.IPC_Unmount_All_Drives);
} };
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'; import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import { createCommonReducer } from '../reducers/common_reducer'; import { createCommonReducer } from '../reducers/common_reducer';
import { downloadReducer } from '../reducers/download_reducer'; import { downloadReducer } from '../reducers/download_reducer';
import { errorReducer } from '../reducers/error_reducer'; import { errorReducer } from '../reducers/error_reducer';
import { installReducer } from '../reducers/install_reducer'; import { installReducer } from '../reducers/install_reducer';
import { createMountReducer } from '../reducers/mount_reducer'; import { createMountReducer } from '../reducers/mount_reducer';
import { pinnedManagerReducer } from '../reducers/pinned_manager_reducer';
import { releaseVersionReducer } from '../reducers/release_version_reducer'; import { releaseVersionReducer } from '../reducers/release_version_reducer';
import { skynetReducer } from '../reducers/skynet_reducer'; import { skynetReducer } from '../reducers/skynet_reducer';
import {pinnedManagerReducer} from '../reducers/pinned_manager_reducer'
export default function createAppStore(platformInfo, version, state) { export default function createAppStore(platformInfo, version, state) {
const reducer = { const reducer = {
@@ -25,6 +26,6 @@ export default function createAppStore(platformInfo, version, state) {
return configureStore({ return configureStore({
reducer, reducer,
middleware, 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); setWindowVisibility(true);
}); });
ipcMain.on(Constants.IPC_Show_Window + '_sync', event => { ipcMain.on(Constants.IPC_Show_Window + '_sync', (event) => {
setWindowVisibility(true); setWindowVisibility(true);
event.returnValue = true; event.returnValue = true;
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -11,10 +11,15 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
Config: data.Data, Config: data.Data,
}); });
} else { } 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); standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error);
}); });
}); });
@@ -27,8 +32,13 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
Template: data, Template: data,
}); });
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error); standardIPCReply(
event,
Constants.IPC_Get_Config_Template_Reply,
{},
error
);
}); });
}); });
@@ -36,12 +46,24 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
const setConfigValue = (i) => { const setConfigValue = (i) => {
if (i < data.Items.length) { if (i < data.Items.length) {
helpers 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(() => { .then(() => {
setConfigValue(++i); setConfigValue(++i);
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}, error); standardIPCReply(
event,
Constants.IPC_Set_Config_Values_Reply,
{},
error
);
}); });
} else { } else {
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}); standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {});
@@ -51,6 +73,4 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -5,43 +5,42 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => { ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => {
helpers helpers
.checkDaemonVersion(data.Version, data.Provider) .checkDaemonVersion(data.Version, data.Provider)
.then(code => { .then((code) => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, { standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, {
Valid: (code === 0), Valid: code === 0,
Code: code, Code: code,
}); });
}) })
.catch(e => { .catch((e) => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, { standardIPCReply(
event,
Constants.IPC_Check_Daemon_Version_Reply,
{
Valid: false, Valid: false,
}, e); },
e
);
}); });
}); });
ipcMain.on(Constants.IPC_Check_Daemon_Version + '_sync', (event, data) => { ipcMain.on(Constants.IPC_Check_Daemon_Version + '_sync', (event, data) => {
helpers helpers
.checkDaemonVersion(data.Version, data.Provider) .checkDaemonVersion(data.Version, data.Provider)
.then(code => { .then((code) => {
event.returnValue = { event.returnValue = {
data: { data: {
Success: true, Success: true,
Valid: (code === 0), Valid: code === 0,
Code: code, Code: code,
}, },
}; };
}) })
.catch(e => { .catch((e) => {
event.returnValue = { event.returnValue = {
data: { data: { Error: e.toString(), Success: false, Valid: false },
Error: e.toString(),
Success: false,
Valid: false
},
}; };
}); });
}); });
}; };
module.exports = { module.exports = { addListeners };
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 { try {
const ls = fs.lstatSync(data.File); const ls = fs.lstatSync(data.File);
event.returnValue = { event.returnValue = {
@@ -30,12 +32,11 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}; };
} catch (e) { } catch (e) {
event.returnValue = { event.returnValue = {
data: { data: { Exists: false },
Exists: false
},
}; };
} }
}); }
);
ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => { ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => {
if (data.Source.toLowerCase().endsWith('.dmg')) { if (data.Source.toLowerCase().endsWith('.dmg')) {
@@ -47,11 +48,16 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
URL: data.URL, URL: data.URL,
}); });
}) })
.catch(error=> { .catch((error) => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source, Source: data.Source,
URL: data.URL, URL: data.URL,
}, error); },
error
);
}); });
} else { } else {
const execInstall = () => { const execInstall = () => {
@@ -63,17 +69,22 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
URL: data.URL, URL: data.URL,
}); });
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source, Source: data.Source,
URL: data.URL, URL: data.URL,
}, error); },
error
);
}); });
}; };
if (data.IsWinFSP) { if (data.IsWinFSP) {
helpers helpers
.performWindowsUninstall(Constants.WINFSP_VERSION_NAMES) .performWindowsUninstall(Constants.WINFSP_VERSION_NAMES)
.then(uninstalled => { .then((uninstalled) => {
if (uninstalled) { if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
RebootRequired: true, RebootRequired: true,
@@ -84,11 +95,16 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
execInstall(); execInstall();
} }
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source, Source: data.Source,
URL: data.URL, URL: data.URL,
}, error); },
error
);
}); });
} else { } else {
execInstall(); execInstall();
@@ -97,6 +113,4 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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