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 =
pathname: path.join(__dirname, '../build/index.html'), process.env.ELECTRON_START_URL ||
protocol: 'file:', url.format({
slashes: true pathname: path.join(__dirname, '../build/index.html'),
}); protocol: 'file:',
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(
cleanup_releases: cleanupReleases, settingFile,
launch_hidden: launchHidden, JSON.stringify({
platform_override: PlatformIPC.getPlatformOverride(), cleanup_releases: cleanupReleases,
}), 'utf-8'); launch_hidden: launchHidden,
} catch (e) { platform_override: PlatformIPC.getPlatformOverride(),
} }),
'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

@@ -2,7 +2,7 @@ import React from 'react';
import './App.css'; import './App.css';
import Box from './components/UI/Box/Box'; import Box from './components/UI/Box/Box';
import Configuration from './containers/Configuration/Configuration'; import Configuration from './containers/Configuration/Configuration';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import DependencyList from './components/DependencyList/DependencyList'; import DependencyList from './components/DependencyList/DependencyList';
import DownloadProgress from './components/DownloadProgress/DownloadProgress'; import DownloadProgress from './components/DownloadProgress/DownloadProgress';
import ErrorDetails from './components/ErrorDetails/ErrorDetails'; import ErrorDetails from './components/ErrorDetails/ErrorDetails';
@@ -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 (
return this.props.ProviderState[k] !== prevProps.ProviderState[k]; Object.keys(this.props.ProviderState).filter((k) => {
}).length > 0) { return this.props.ProviderState[k] !== prevProps.ProviderState[k];
}).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(
version={selectedVersion} showConfig,
s3Supported={s3Supported} <Configuration
remoteSupported={remoteSupported}/>); version={selectedVersion}
const pinnedManagerDisplay = createModalConditionally(showPinnedManager, <PinnedManager s3Supported={s3Supported}
version={selectedVersion}/>) remoteSupported={remoteSupported}
const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo/>); />
const dependencyDisplay = createModalConditionally(showDependencies, );
<DependencyList/>, false, this.props.InstallActive); const pinnedManagerDisplay = createModalConditionally(
const downloadDisplay = createModalConditionally(this.props.DownloadActive, showPinnedManager,
<DownloadProgress/>, false, true); <PinnedManager version={selectedVersion} />
const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true); );
const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true); const confirmDisplay = createModalConditionally(
const newReleasesDisplay = createModalConditionally(showNewReleases, <NewReleases/>); this.props.DisplayConfirmYesNo,
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot/>); <YesNo />
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, );
<SelectAppPlatform/>); const dependencyDisplay = createModalConditionally(
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI/>); showDependencies,
const importDisplay = createModalConditionally(showSkynetImport, <SkynetImport <DependencyList />,
version={selectedVersion}/>); false,
const exportDisplay = createModalConditionally(showSkynetExport, <SkynetExport this.props.InstallActive
version={selectedVersion}/>) );
const appBusyDisplay = createModalConditionally(this.props.AppBusy, const downloadDisplay = createModalConditionally(
<ApplicationBusy/>, false, true, this.props.AppBusyTransparent); this.props.DownloadActive,
<DownloadProgress />,
false,
true
);
const errorDisplay = createModalConditionally(
this.props.DisplayError,
<ErrorDetails />,
true
);
const infoDisplay = createModalConditionally(
this.props.DisplayInfo,
<InfoDetails />,
true
);
const newReleasesDisplay = createModalConditionally(
showNewReleases,
<NewReleases />
);
const rebootDisplay = createModalConditionally(
this.props.RebootRequired,
<Reboot />
);
const selectAppPlatformDisplay = createModalConditionally(
this.props.DisplaySelectAppPlatform,
<SelectAppPlatform />
);
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI />);
const importDisplay = createModalConditionally(
showSkynetImport,
<SkynetImport version={selectedVersion} />
);
const exportDisplay = createModalConditionally(
showSkynetExport,
<SkynetExport version={selectedVersion} />
);
const appBusyDisplay = createModalConditionally(
this.props.AppBusy,
<ApplicationBusy />,
false,
true,
this.props.AppBusyTransparent
);
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++}
remoteSupported={remoteSupported} >
scPrimeSupported={scPrimeSupported} <MountItems
siaSupported={siaSupported} s3Supported={s3Supported}
skynetSupported={skynetSupported}/> remoteSupported={remoteSupported}
scPrimeSupported={scPrimeSupported}
siaSupported={siaSupported}
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' }}
<Loading/> key={'md_' + key++}
>
<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
colSpan={'remain'} col={0}
row={0} colSpan={'remain'}
rowSpan={'remain'} row={0}
text={'Repertory UI v' + this.props.Version} rowSpan={'remain'}
textAlign={'center'} text={'Repertory UI v' + this.props.Version}
type={'Heading1'}/> textAlign={'center'}
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

@@ -3,18 +3,26 @@ import Box from '../UI/Box/Box';
import Loader from 'react-loader-spinner'; import Loader from 'react-loader-spinner';
import Text from '../UI/Text/Text'; import Text from '../UI/Text/Text';
const ApplicationBusy = ({title}) => { const ApplicationBusy = ({ title }) => {
return ( return (
<Box dxStyle={{padding: 'var(--default_spacing)'}}> <Box dxStyle={{ padding: 'var(--default_spacing)' }}>
<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
height={32} style={{
width={32} paddingLeft: 'calc(50% - 16px)',
type='TailSpin'/> paddingTop: 'var(--default_spacing)',
}}
>
<Loader
color={'var(--heading_text_color)'}
height={32}
width={32}
type="TailSpin"
/>
</div> </div>
</Box> </Box>
); );

View File

@@ -1,17 +1,18 @@
import React from 'react'; import React from 'react';
import './Dependency.css'; 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
className={'DependencyLink'} href={'#'}
onClick={()=>{props.onDownload(); return false;}}><u>Install</u></a> : className={'DependencyLink'}
'Installing...'} onClick={() => {
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

@@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import './DependencyList.css'; import './DependencyList.css';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import * as Constants from '../../constants'; 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(
const items = props.MissingDependencies.map((k, i)=> { mapStateToProps,
mapDispatchToProps
)((props) => {
const items = props.MissingDependencies.map((k, i) => {
return ( return (
<Dependency key={i} <Dependency
name={k.display} key={i}
onDownload={()=>props.downloadItem(extractFileNameFromURL(k.download), Constants.INSTALL_TYPES.Dependency, k.download, k.is_winfsp)}/> name={k.display}
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

@@ -1,9 +1,9 @@
import Box from '../UI/Box/Box'; import Box from '../UI/Box/Box';
import {connect} from 'react-redux'; 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
style={{width: '100%'}} max={100.0}
value={props.DownloadProgress}/> id={'download_progress'}
</Box>); style={{ width: '100%' }}
value={props.DownloadProgress}
/>
</Box>
);
}); });

View File

@@ -1,25 +1,29 @@
import React from 'react'; import React from 'react';
import {dismissError} from '../../redux/actions/error_actions'; import { dismissError } from '../../redux/actions/error_actions';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import Box from '../UI/Box/Box'; 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>
<div className={'ErrorDetailsContent'}> <div className={'ErrorDetailsContent'}>
<p>{props.ErrorMessage}</p> <p>{props.ErrorMessage}</p>

View File

@@ -1,23 +1,27 @@
import React from 'react'; import React from 'react';
import {dismissInfo} from '../../redux/actions/error_actions'; import { dismissInfo } from '../../redux/actions/error_actions';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import Box from '../UI/Box/Box'; 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;
@@ -46,20 +50,20 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
}; };
return ( return (
<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

@@ -1,24 +1,27 @@
import React from 'react'; import React from 'react';
import './Reboot.css'; import './Reboot.css';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import Box from '../UI/Box/Box'; 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>
<div className={'RebootContent'}> <div className={'RebootContent'}>
<p>Repertory requires a system reboot to continue.</p> <p>Repertory requires a system reboot to continue.</p>
</div> </div>
<Button clicked={()=>props.rebootSystem()}>Reboot Now</Button> <Button clicked={() => props.rebootSystem()}>Reboot Now</Button>
</Box> </Box>
); );
}); });

View File

@@ -1,16 +1,16 @@
import React from 'react'; import React from 'react';
import './ReleaseVersionDisplay.css'; import './ReleaseVersionDisplay.css';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import DropDown from '../UI/DropDown/DropDown'; import DropDown from '../UI/DropDown/DropDown';
import Grid from '../UI/Grid/Grid'; import Grid from '../UI/Grid/Grid';
import Text from '../UI/Text/Text'; import Text from '../UI/Text/Text';
import Button from '../UI/Button/Button'; import Button from '../UI/Button/Button';
import UpgradeIcon from '../UpgradeIcon/UpgradeIcon'; 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
colSpan={'remain'} col={(dimensions) => (dimensions.columns / 3) * 2}
key={key++} colSpan={'remain'}
rowSpan={4} key={key++}
text={'Activating'} rowSpan={4}
textAlign={'left'} text={'Activating'}
type={'Heading2'}/> textAlign={'left'}
)); type={'Heading2'}
optionsDisplay.push(( />
<Text col={dimensions => (dimensions.columns / 3) * 2} );
colSpan={'remain'} optionsDisplay.push(
key={key++} <Text
row={5} col={(dimensions) => (dimensions.columns / 3) * 2}
rowSpan={7} colSpan={'remain'}
text={text} key={key++}
textAlign={'left'}/> row={5}
)); rowSpan={7}
text={text}
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
colSpan={'remain'} col={(dimensions) => (dimensions.columns / 3) * 2}
key={key++} colSpan={'remain'}
rowSpan={4} key={key++}
text={'Installed'} rowSpan={4}
textAlign={'left'} text={'Installed'}
type={'Heading2'}/> textAlign={'left'}
)); type={'Heading2'}
/>
);
optionsDisplay.push(( optionsDisplay.push(
<Text col={dimensions => (dimensions.columns / 3) * 2} <Text
colSpan={'remain'} col={(dimensions) => (dimensions.columns / 3) * 2}
key={key++} colSpan={'remain'}
row={5} key={key++}
rowSpan={7} row={5}
text={text} rowSpan={7}
textAlign={'left'}/> text={text}
)); textAlign={'left'}
/>
);
} else { } else {
optionsDisplay.push(( optionsDisplay.push(
<Button clicked={handleDownloadRelease} <Button
col={dimensions => (dimensions.columns / 3) * 2} clicked={handleDownloadRelease}
colSpan={20} col={(dimensions) => (dimensions.columns / 3) * 2}
key={key++} colSpan={20}
row={5} key={key++}
rowSpan={7}>Install</Button> row={5}
)); rowSpan={7}
>
Install
</Button>
);
} }
return ( return (
<Grid noScroll> <Grid noScroll>
<Text colSpan={columns=>columns / 3} <Text
rowSpan={4} colSpan={(columns) => columns / 3}
text={'Release'} rowSpan={4}
textAlign={'left'} text={'Release'}
type={'Heading2'}/> textAlign={'left'}
<DropDown changed={handleReleaseChanged} type={'Heading2'}
colSpan={remain=>remain / 3 - 1} />
disabled={disabled} <DropDown
items={Constants.RELEASE_TYPES} changed={handleReleaseChanged}
row={5} colSpan={(remain) => remain / 3 - 1}
rowSpan={7} disabled={disabled}
selected={Constants.RELEASE_TYPES[props.Release]}/> items={Constants.RELEASE_TYPES}
<Text col={dimensions => dimensions.columns / 3} row={5}
colSpan={remain=>remain / 2} rowSpan={7}
rowSpan={4} selected={Constants.RELEASE_TYPES[props.Release]}
text={'Version'} />
textAlign={'left'} <Text
type={'Heading2'}/> col={(dimensions) => dimensions.columns / 3}
<UpgradeIcon available={props.ReleaseUpgradeAvailable} colSpan={(remain) => remain / 2}
col={dimensions => ((dimensions.columns / 3) * 2) - 6} rowSpan={4}
colSpan={4} text={'Version'}
release textAlign={'left'}
rowSpan={4}/> type={'Heading2'}
<DropDown changed={handleVersionChanged} />
col={dimensions => dimensions.columns / 3} <UpgradeIcon
colSpan={remain=>remain / 2 - 1} available={props.ReleaseUpgradeAvailable}
disabled={disabled} col={(dimensions) => (dimensions.columns / 3) * 2 - 6}
items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]} colSpan={4}
row={5} release
rowSpan={7} rowSpan={4}
selected={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion]}/> />
<DropDown
changed={handleVersionChanged}
col={(dimensions) => dimensions.columns / 3}
colSpan={(remain) => remain / 2 - 1}
disabled={disabled}
items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]}
row={5}
rowSpan={7}
selected={
props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][
props.ReleaseVersion
]
}
/>
{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
autoFocus={props.autoFocus} disabled={props.disabled}
className={'Button'} autoFocus={props.autoFocus}
style={props.buttonStyles} className={'Button'}
onClick={props.clicked}>{props.children}</button> style={props.buttonStyles}
onClick={props.clicked}
>
{props.children}
</button>
); );
}; };

View File

@@ -18,12 +18,12 @@ 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;
height: 0; height: 0;
width: 0; width: 0;
} }
/* Create a custom checkbox */ /* Create a custom checkbox */
@@ -41,8 +41,8 @@ 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);
} }
/* When the checkbox is checked, add a blue background */ /* When the checkbox is checked, add a blue background */
@@ -52,9 +52,9 @@ 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;
} }
/* Show the CheckBoxCheckMark when checked */ /* Show the CheckBoxCheckMark when checked */

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}
autoFocus={props.autoFocus} <input
disabled={props.disabled} checked={JSON.parse(props.checked)}
onChange={props.changed} autoFocus={props.autoFocus}
type='checkbox'/> disabled={props.disabled}
<span className='CheckBoxCheckMark'/> onChange={props.changed}
type="checkbox"
/>
<span className="CheckBoxCheckMark" />
</label> </label>
</div> </div>
); );

View File

@@ -6,18 +6,19 @@
padding: 0; padding: 0;
} }
.DropDownSelect, .DropDownSelect.Alt { .DropDownSelect,
width: 100%; .DropDownSelect.Alt {
height: 100%; width: 100%;
display: block; height: 100%;
margin: 0; display: block;
padding: 2px; margin: 0;
border-radius: var(--border_radius); padding: 2px;
background: var(--control_background); border-radius: var(--border_radius);
border: none; background: var(--control_background);
color: var(--text_color); border: none;
box-sizing: border-box; color: var(--text_color);
outline: none !important; box-sizing: border-box;
outline: none !important;
} }
.DropDownSelect.Auto { .DropDownSelect.Auto {

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

@@ -1,4 +1,4 @@
import React, {Component} from 'react'; import React, { Component } from 'react';
import './Grid.css'; import './Grid.css';
import GridComponent from './GridComponent/GridComponent'; import GridComponent from './GridComponent/GridComponent';
@@ -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
col={col} row={row}
rowSpan={rowSpan} col={col}
colSpan={colSpan} rowSpan={rowSpan}
key={'gc_' + i}> colSpan={colSpan}
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

@@ -7,12 +7,13 @@
} }
.LoadingContent { .LoadingContent {
margin: 0; margin: 0;
padding: 0; padding: 0;
position: relative; position: relative;
top: 50%; left: 50%; top: 50%;
transform: translate(-50%,-50%); left: 50%;
width: 28px; transform: translate(-50%, -50%);
height: 28px; width: 28px;
box-sizing: border-box; height: 28px;
box-sizing: border-box;
} }

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
height={28} color={'var(--heading_text_color)'}
width={28} height={28}
type='ThreeDots'/> width={28}
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,28 +1,25 @@
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) {
styleList.push('Text' + props.type); styleList.push('Text' + props.type);
} }
let style = {...props.style}; let style = { ...props.style };
if (props.textAlign) { if (props.textAlign) {
style['textAlign'] = props.textAlign.toLowerCase(); style['textAlign'] = props.textAlign.toLowerCase();
} }
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 {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
const UpgradeIcon = props => { import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import ReactTooltip from 'react-tooltip';
const UpgradeIcon = (props) => {
const 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'}>
( <p data-tip="" data-for={placement}>
<div className={'UpgradeIconOwner'}> <a href={'#'} className={styles.join(' ')} onClick={props.clicked}>
<p data-tip='' data-for={placement}> <FontAwesomeIcon icon={faExclamationTriangle} />
<a href={'#'} </a>
className={styles.join(' ')} </p>
onClick={props.clicked}> <ReactTooltip id={placement} place={placement}>
<FontAwesomeIcon icon={faExclamationTriangle}/> {toolTipText}
</a> </ReactTooltip>
</p> </div>
<ReactTooltip id={placement} place={placement}>{toolTipText}</ReactTooltip> ) : null;
</div>
)
: null;
}; };
export default UpgradeIcon; export default UpgradeIcon;

View File

@@ -1,55 +1,72 @@
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import Button from '../UI/Button/Button'; import Button from '../UI/Button/Button';
import Box from '../UI/Box/Box'; import Box from '../UI/Box/Box';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import React from 'react'; import React from 'react';
import './UpgradeUI.css'; 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 => {
return {
downloadItem: (name, type, urls) => dispatch(downloadItem(name, type, urls)),
setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)),
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(props => { const mapDispatchToProps = (dispatch) => {
return {
downloadItem: (name, type, urls) =>
dispatch(downloadItem(name, type, urls)),
setDismissUIUpgrade: (dismiss) => dispatch(setDismissUIUpgrade(dismiss)),
};
};
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

@@ -1,41 +1,59 @@
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import Button from '../UI/Button/Button'; import Button from '../UI/Button/Button';
import Box from '../UI/Box/Box'; import Box from '../UI/Box/Box';
import React from 'react'; 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 => {
return {
hideConfirmYesNo: confirmed => dispatch(hideConfirmYesNo(confirmed)),
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(props => { const mapDispatchToProps = (dispatch) => {
return {
hideConfirmYesNo: (confirmed) => dispatch(hideConfirmYesNo(confirmed)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)((props) => {
return ( return (
<Box dxStyle={{minWidth: '180px', height: 'auto', padding: 'var(--default_spacing)'}}> <Box
<div style={{width: '100%', height: 'auto'}}> dxStyle={{
<h1 style={{width: '100%', textAlign: 'center'}}>{props.Title}</h1> minWidth: '180px',
height: 'auto',
padding: 'var(--default_spacing)',
}}
>
<div style={{ width: '100%', height: 'auto' }}>
<h1 style={{ width: '100%', textAlign: 'center' }}>{props.Title}</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
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

@@ -1,21 +1,21 @@
Object.defineProperty(exports, '__esModule', {value : true}); Object.defineProperty(exports, '__esModule', { value: true });
exports.DEV_PUBLIC_KEY = exports.DEV_PUBLIC_KEY =
'-----BEGIN PUBLIC KEY-----\n' + '-----BEGIN PUBLIC KEY-----\n' +
'MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEKfZmq5mMAtD4kSt2Gc/5J\n' + 'MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEKfZmq5mMAtD4kSt2Gc/5J\n' +
'H+HHTYtUZE6YYvsvz8TNG/bNL67ZtNRyaoMyhLTfIN4rPBNLUfD+owNS+u5Yk+lS\n' + 'H+HHTYtUZE6YYvsvz8TNG/bNL67ZtNRyaoMyhLTfIN4rPBNLUfD+owNS+u5Yk+lS\n' +
'ZLYyOuhoCZIFefayYqKLr42G8EeuRbx0IMzXmJtN0a4rqxlWhkYufJubpdQ+V4DF\n' + 'ZLYyOuhoCZIFefayYqKLr42G8EeuRbx0IMzXmJtN0a4rqxlWhkYufJubpdQ+V4DF\n' +
'oeupcPdIATaadCKVeZC7A0G0uaSwoiAVMG5dZqjQW7F2LoQm3PhNkPvAybIJ6vBy\n' + 'oeupcPdIATaadCKVeZC7A0G0uaSwoiAVMG5dZqjQW7F2LoQm3PhNkPvAybIJ6vBy\n' +
'LqdBegS1JrDn43x/pvQHzLO+l+FIG23D1F7iF+yZm3DkzBdcmi/mOMYs/rXZpBym\n' + 'LqdBegS1JrDn43x/pvQHzLO+l+FIG23D1F7iF+yZm3DkzBdcmi/mOMYs/rXZpBym\n' +
'2/kTuSGh5buuJCeyOwR8N3WdvXw6+KHMU/wWU8qTCTT87mYbzH4YR8HgkjkLHxAO\n' + '2/kTuSGh5buuJCeyOwR8N3WdvXw6+KHMU/wWU8qTCTT87mYbzH4YR8HgkjkLHxAO\n' +
'5waHK6vMu0TxugCdJmVV6BSbiarJsh66VRosn7+6hlq6AdgksxqCeNELZBS+LBki\n' + '5waHK6vMu0TxugCdJmVV6BSbiarJsh66VRosn7+6hlq6AdgksxqCeNELZBS+LBki\n' +
'tb5hKyL+jNZnaHiR0U7USWtmnqZG6FVVRzlCnxP7tZo5O5Ex9AAFGz5JzOzsFNbv\n' + 'tb5hKyL+jNZnaHiR0U7USWtmnqZG6FVVRzlCnxP7tZo5O5Ex9AAFGz5JzOzsFNbv\n' +
'xwQ0zqaTQOze+MJbkda7JfRoC6TncD0+3hoXsiaF4mCn8PqUCn0DwhglcRucZlST\n' + 'xwQ0zqaTQOze+MJbkda7JfRoC6TncD0+3hoXsiaF4mCn8PqUCn0DwhglcRucZlST\n' +
'ZvDNDo1WAtxPJebb3aS6uymNhBIquQbVAWxVO4eTrOYEgutxwkHE3yO3is+ogp8d\n' + 'ZvDNDo1WAtxPJebb3aS6uymNhBIquQbVAWxVO4eTrOYEgutxwkHE3yO3is+ogp8d\n' +
'xot7f/+vzlbsbIDyuZBDe0fFkbTIMTU48QuUUVZpRKmKZTHQloz4EHqminbfX1sh\n' + 'xot7f/+vzlbsbIDyuZBDe0fFkbTIMTU48QuUUVZpRKmKZTHQloz4EHqminbfX1sh\n' +
'M7wvDkpJEtqbc0VnG/BukUzP6e7Skvgc7eF1sI3+8jH8du2rivZeZAl7Q2f+L9JA\n' + 'M7wvDkpJEtqbc0VnG/BukUzP6e7Skvgc7eF1sI3+8jH8du2rivZeZAl7Q2f+L9JA\n' +
'BY9pjaxttxsud7V5jeFi4tKuDHi21/XhSjlJK2c2C4AiUEK5/WhtGbQ5JjmcOjRq\n' + 'BY9pjaxttxsud7V5jeFi4tKuDHi21/XhSjlJK2c2C4AiUEK5/WhtGbQ5JjmcOjRq\n' +
'yXFRqLlerzOcop2kbtU3Ar230wOx3Dj23Wg8++lV3LU4U9vMR/t0qnSbCSGJys7m\n' + 'yXFRqLlerzOcop2kbtU3Ar230wOx3Dj23Wg8++lV3LU4U9vMR/t0qnSbCSGJys7m\n' +
'ax2JpFlTwj/0wYuTlVFoNQHZJ1cdfyRiRBY4Ou7XO0W5hcBBKiYsC+neEeMMHdCe\n' + 'ax2JpFlTwj/0wYuTlVFoNQHZJ1cdfyRiRBY4Ou7XO0W5hcBBKiYsC+neEeMMHdCe\n' +
'iTDIW/ojcVTdFovl+sq3n1u4SBknE90JC/3H+TPE1s2iB+fwORVg0KPosQSNDS0A\n' + 'iTDIW/ojcVTdFovl+sq3n1u4SBknE90JC/3H+TPE1s2iB+fwORVg0KPosQSNDS0A\n' +
'7iK6AZCDC3YooFo+OzHkYMt9uLkXiXMSLx70az+qlIwOzVHKxCo7W/QpeKCXUCRZ\n' + '7iK6AZCDC3YooFo+OzHkYMt9uLkXiXMSLx70az+qlIwOzVHKxCo7W/QpeKCXUCRZ\n' +
'MMdlYEUs1PC8x2qIRUEVHuJ0XMTKNyOHmzVLuLK93wUWbToh+rdDxnbhX+emuESn\n' + 'MMdlYEUs1PC8x2qIRUEVHuJ0XMTKNyOHmzVLuLK93wUWbToh+rdDxnbhX+emuESn\n' +
@@ -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/' +
'/detect_linux2.sh'; _REPERTORY_BRANCH +
'/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',
@@ -68,55 +70,42 @@ 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: '',
skynet : '-sk', skynet: '-sk',
scprime : '-sp', scprime: '-sp',
s3 : '-s3', s3: '-s3',
}; };
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',
Release : 'release', Release: 'release',
TestRelease : 'test_release', TestRelease: 'test_release',
Upgrade : 'upgrade', Upgrade: 'upgrade',
}; };
exports.IPC_Browse_Directory = 'browse_directory'; exports.IPC_Browse_Directory = 'browse_directory';

View File

@@ -1,29 +1,32 @@
import React from 'react'; import React from 'react';
import {Component} from 'react'; import { Component } from 'react';
import './AddMount.css'; import './AddMount.css';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import Button from '../../components/UI/Button/Button'; import Button from '../../components/UI/Button/Button';
import Box from '../../components/UI/Box/Box'; 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,207 +43,288 @@ const default_state = {
Token: '', Token: '',
}; };
export default connect(mapStateToProps, mapDispatchToProps)(class extends Component { export default connect(
state = { mapStateToProps,
...default_state, mapDispatchToProps
}; )(
class extends Component {
state = {
...default_state,
};
addRemoteMount = () => { addRemoteMount = () => {
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 {
const provider = 'Remote' + this.state.HostNameOrIp + ':' + this.state.Port;
if (this.props.RemoteMounts.includes(provider)) {
this.props.notifyError('Remote host already exists');
} else { } else {
this.setState({ const provider =
DisplayRemote: false 'Remote' + this.state.HostNameOrIp + ':' + this.state.Port;
}, () => { if (this.props.RemoteMounts.includes(provider)) {
this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token); this.props.notifyError('Remote host already exists');
this.setState({ } else {
...default_state, this.setState(
}); {
}); DisplayRemote: false,
},
() => {
this.props.addRemoteMount(
this.state.HostNameOrIp,
this.state.Port,
this.state.Token
);
this.setState({
...default_state,
});
}
);
}
} }
} };
};
addS3Mount = () => { addS3Mount = () => {
if (this.state.Name.length === 0) { if (this.state.Name.length === 0) {
this.props.notifyError('Name cannot be empty.'); this.props.notifyError('Name cannot be empty.');
} 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 {
const provider = 'S3' + this.state.Name;
if (this.props.S3Mounts.includes(provider)) {
this.props.notifyError('Remote host already exists');
} else { } else {
this.setState({ const provider = 'S3' + this.state.Name;
DisplayS3: false if (this.props.S3Mounts.includes(provider)) {
}, () => { this.props.notifyError('Remote host already exists');
this.props.addS3Mount(this.state.Name, this.state.AccessKey, this.state.SecretKey, } else {
this.state.Region, this.state.BucketName, Constants.S3_PROVIDER_URL[this.state.Provider]); this.setState(
this.setState({ {
...default_state, DisplayS3: false,
}); },
}); () => {
this.props.addS3Mount(
this.state.Name,
this.state.AccessKey,
this.state.SecretKey,
this.state.Region,
this.state.BucketName,
Constants.S3_PROVIDER_URL[this.state.Provider]
);
this.setState({
...default_state,
});
}
);
}
} }
};
handleAddS3Mount = () => {
this.setState({
DisplayRemote: false,
DisplayS3: true,
});
};
handleAddRemoteMount = () => {
this.setState({
DisplayRemote: true,
DisplayS3: false,
});
};
render() {
const displayAddRemote = createModalConditionally(
this.state.DisplayRemote,
<Box
dxDark
dxStyle={{
width: 'auto',
height: 'auto',
padding: 'var(--default_spacing)',
}}
>
<h1
style={{
textAlign: 'center',
paddingBottom: 'var(--default_spacing)',
}}
>
Add Remote Mount
</h1>
<Text text={'Hostname or IP'} textAlign={'left'} type={'Heading2'} />
<input
onChange={(e) =>
this.setState({ HostNameOrIp: e.target.value.trim() })
}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.HostNameOrIp}
/>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<Text text={'Port'} textAlign={'left'} type={'Heading2'} />
<input
max={65535}
min={1025}
onChange={(e) => this.setState({ Port: e.target.value })}
className={'ConfigurationItemInput'}
type={'number'}
value={this.state.Port}
/>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<Text text={'Remote Token'} textAlign={'left'} type={'Heading2'} />
<input
onChange={(e) => this.setState({ Token: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Token}
/>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.addRemoteMount()}
>
OK
</Button>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.setState({ DisplayRemote: false })}
>
Cancel
</Button>
</div>
</Box>
);
const displayAddS3 = createModalConditionally(
this.state.DisplayS3,
<Box
dxDark
dxStyle={{
width: 'auto',
height: 'auto',
padding: 'var(--default_spacing)',
}}
>
<h1
style={{
textAlign: 'center',
paddingBottom: 'var(--default_spacing)',
}}
>
Add S3 Mount
</h1>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Name'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Provider'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ Name: e.target.value.trim() })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.Name}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<DropDown
changed={(e) => this.setState({ Provider: e.target.value })}
items={Constants.S3_PROVIDER_LIST}
selected={this.state.Provider}
/>
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text
text={'Bucket Name (optional)'}
textAlign={'left'}
type={'Heading2'}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Region'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ BucketName: e.target.value })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.BucketName}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ Region: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Region}
/>
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Access Key'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Secret Key'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ AccessKey: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.AccessKey}
/>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ SecretKey: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.SecretKey}
/>
</div>
<div style={{ paddingTop: 'calc(var(--default_spacing) * 2)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div style={{ width: '200%' }} />
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.addS3Mount()}
>
OK
</Button>
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Button
buttonStyles={{ width: '100%' }}
clicked={() => this.setState({ DisplayS3: false })}
>
Cancel
</Button>
</div>
</Box>
);
return (
<div className={'AddMount'}>
{displayAddRemote}
{displayAddS3}
<div className={'AddMountButtons'}>
{this.props.remoteSupported ? (
<Button
className={'AddMountButton'}
clicked={this.handleAddRemoteMount}
>
Add Remote Mount
</Button>
) : null}
{this.props.remoteSupported && this.props.s3Supported ? (
<div style={{ paddingRight: 'var(--default_spacing)' }} />
) : null}
{this.props.s3Supported ? (
<Button
className={'AddMountButton'}
clicked={this.handleAddS3Mount}
>
Add S3 Mount
</Button>
) : null}
</div>
</div>
);
} }
};
handleAddS3Mount = () => {
this.setState({
DisplayRemote: false,
DisplayS3: true,
});
};
handleAddRemoteMount = () => {
this.setState({
DisplayRemote: true,
DisplayS3: false,
});
};
render() {
const displayAddRemote = createModalConditionally(this.state.DisplayRemote, (
<Box dxDark
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add Remote
Mount</h1>
<Text text={'Hostname or IP'}
textAlign={'left'}
type={'Heading2'}/>
<input onChange={e => this.setState({HostNameOrIp: e.target.value.trim()})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.HostNameOrIp}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<Text text={'Port'}
textAlign={'left'}
type={'Heading2'}/>
<input max={65535}
min={1025}
onChange={e => this.setState({Port: e.target.value})}
className={'ConfigurationItemInput'}
type={'number'}
value={this.state.Port}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<Text text={'Remote Token'}
textAlign={'left'}
type={'Heading2'}/>
<input onChange={e => this.setState({Token: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Token}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<div style={{display: 'flex', flexDirection: 'row'}}>
<Button buttonStyles={{width: '100%'}}
clicked={() => this.addRemoteMount()}>OK</Button>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({DisplayRemote: false})}>Cancel</Button>
</div>
</Box>
));
const displayAddS3 = createModalConditionally(this.state.DisplayS3, (
<Box dxDark
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add S3
Mount</h1>
<div style={{display: 'flex', flexDirection: 'row'}}>
<Text text={'Name'}
textAlign={'left'}
type={'Heading2'}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<Text text={'Provider'}
textAlign={'left'}
type={'Heading2'}/>
</div>
<div style={{display: 'flex', flexDirection: 'row'}}>
<input onChange={e => this.setState({Name: e.target.value.trim()})}
className={'ConfigurationItemInput'}
style={{width: '100%'}}
type={'text'}
value={this.state.Name}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<DropDown changed={e => this.setState({Provider: e.target.value})}
items={Constants.S3_PROVIDER_LIST}
selected={this.state.Provider}/>
</div>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<div style={{display: 'flex', flexDirection: 'row'}}>
<Text text={'Bucket Name (optional)'}
textAlign={'left'}
type={'Heading2'}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<Text text={'Region'}
textAlign={'left'}
type={'Heading2'}/>
</div>
<div style={{display: 'flex', flexDirection: 'row'}}>
<input onChange={e => this.setState({BucketName: e.target.value})}
className={'ConfigurationItemInput'}
style={{width: '100%'}}
type={'text'}
value={this.state.BucketName}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<input onChange={e => this.setState({Region: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Region}/>
</div>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<div style={{display: 'flex', flexDirection: 'row'}}>
<Text text={'Access Key'}
textAlign={'left'}
type={'Heading2'}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<Text text={'Secret Key'}
textAlign={'left'}
type={'Heading2'}/>
</div>
<div style={{display: 'flex', flexDirection: 'row'}}>
<input onChange={e => this.setState({AccessKey: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.AccessKey}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<input onChange={e => this.setState({SecretKey: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.SecretKey}/>
</div>
<div style={{paddingTop: 'calc(var(--default_spacing) * 2)'}}/>
<div style={{display: 'flex', flexDirection: 'row'}}>
<div style={{width: '200%'}}/>
<Button buttonStyles={{width: '100%'}}
clicked={() => this.addS3Mount()}>OK</Button>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({DisplayS3: false})}>Cancel</Button>
</div>
</Box>
));
return (
<div className={'AddMount'}>
{displayAddRemote}
{displayAddS3}
<div className={'AddMountButtons'}>
{this.props.remoteSupported ?
<Button className={'AddMountButton'}
clicked={this.handleAddRemoteMount}>Add Remote Mount</Button> : null}
{this.props.remoteSupported && this.props.s3Supported ?
<div style={{paddingRight: 'var(--default_spacing)'}}/> : null}
{this.props.s3Supported ?
<Button className={'AddMountButton'}
clicked={this.handleAddS3Mount}>Add S3 Mount</Button> : null}
</div>
</div>
);
} }
}); );

View File

@@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import './Configuration.css'; import './Configuration.css';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
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 ConfigurationItem from './ConfigurationItem/ConfigurationItem'; import ConfigurationItem from './ConfigurationItem/ConfigurationItem';
import Modal from '../../components/UI/Modal/Modal'; import Modal from '../../components/UI/Modal/Modal';
import IPCContainer from '../IPCContainer/IPCContainer'; import IPCContainer from '../IPCContainer/IPCContainer';
import {displayConfiguration} from '../../redux/actions/mount_actions'; import { displayConfiguration } from '../../redux/actions/mount_actions';
import {notifyError} from '../../redux/actions/error_actions'; import { notifyError } from '../../redux/actions/error_actions';
import {displayPinnedManager} from '../../redux/actions/pinned_manager_actions'; import { displayPinnedManager } from '../../redux/actions/pinned_manager_actions';
const Constants = require('../../constants'); const Constants = require('../../constants');
@@ -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,55 +106,57 @@ 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:
value: (template[key] && (template[key].type === 'object')) ? template[key] && template[key].type === 'object'
config[key] : ? config[key]
(template[key] && (template[key].type === 'string_array')) ? : template[key] && template[key].type === 'string_array'
config[key] : ? config[key]
config[key].toString(), : 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;
} }
return ret; return ret;
}); });
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, {
ItemList: list.ItemList, IsRemoteMount: isRemoteMount,
ObjectLookup: objectLookup, ItemList: list.ItemList,
OriginalItemList: itemListCopy, ObjectLookup: objectLookup,
OriginalObjectLookup: objectLookupCopy, OriginalItemList: itemListCopy,
}, () => { 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, { },
Provider: this.props.DisplayConfiguration, () => {
Remote: this.props.DisplayRemoteConfiguration, this.sendRequest(Constants.IPC_Get_Config, {
S3: this.props.DisplayS3Configuration, Provider: this.props.DisplayConfiguration,
Version: this.props.version, Remote: this.props.DisplayRemoteConfiguration,
}); S3: this.props.DisplayS3Configuration,
}); 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,77 +236,102 @@ class Configuration extends IPCContainer {
}; };
saveAndClose = () => { saveAndClose = () => {
this.setState({ this.setState(
Saving: true, {
}, () => { Saving: true,
const changedItems = []; },
for (const item of this.state.ChangedItems) { () => {
changedItems.push({ const changedItems = [];
Name: item.label, for (const item of this.state.ChangedItems) {
Value: item.type === 'string_array' ? changedItems.push({
item.value.join(';') : Name: item.label,
item.value, Value:
}); item.type === 'string_array' ? item.value.join(';') : item.value,
} });
}
if (this.state.ChangedObjectLookup) { if (this.state.ChangedObjectLookup) {
for (const key of Object.keys(this.state.ChangedObjectLookup)) { for (const key of Object.keys(this.state.ChangedObjectLookup)) {
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,
});
}
} }
} }
}
this.sendRequest(Constants.IPC_Set_Config_Values, { this.sendRequest(Constants.IPC_Set_Config_Values, {
Items: changedItems, Items: changedItems,
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,
}); });
}); }
);
}; };
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
</td> clicked={this.saveAndClose}
<td align='center' width='50%'><Button clicked={this.props.hideConfiguration} disabled={this.state.Saving}
disabled={this.state.Saving}>No</Button></td> >
</tr> Yes
</Button>
</td>
<td align="center" width="50%">
<Button
clicked={this.props.hideConfiguration}
disabled={this.state.Saving}
>
No
</Button>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</Box> </Box>
@@ -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 !k.advanced ||
return ( (this.state.ShowAdvanced && k.advanced && !k.remote) ||
(!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ? this.showRemoteConfigItem(k, this.state.ObjectLookup[key]) ? (
<ConfigurationItem advanced={k.advanced} <ConfigurationItem
autoFocus={shouldFocus} advanced={k.advanced}
changed={e => this.handleObjectItemChanged(e, key, i)} autoFocus={shouldFocus}
grouping={key} changed={(e) => this.handleObjectItemChanged(e, key, i)}
items={this.state.Template[key].template[k.label].items} grouping={key}
key={i} items={this.state.Template[key].template[k.label].items}
label={k.label} key={i}
readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))} label={k.label}
template={this.state.Template[key].template[k.label]} readOnly={
value={k.value}/> : this.state.IsRemoteMount &&
null) (k.label === 'RemoteHostNameOrIp' ||
}) 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
autoFocus={shouldFocus} advanced={k.advanced}
changed={e => this.handleItemChanged(e, i)} autoFocus={shouldFocus}
grouping={'Settings'} changed={(e) => this.handleItemChanged(e, i)}
items={this.state.Template[k.label].items} grouping={'Settings'}
key={i} items={this.state.Template[k.label].items}
label={k.label} key={i}
template={this.state.Template[k.label]} label={k.label}
value={k.value}/> : template={this.state.Template[k.label]}
null value={k.value}
); />
) : 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
float: 'right', style={{
margin: 0, float: 'right',
padding: 0, margin: 0,
marginTop: '-4px', padding: 0,
boxSizing: 'border-box', marginTop: '-4px',
display: 'block' boxSizing: 'border-box',
}}> display: 'block',
<a href={'#'} }}
onClick={this.checkSaveRequired} >
style={{cursor: 'pointer'}}>X</a> <a
href={'#'}
onClick={this.checkSaveRequired}
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
clicked={() => { buttonStyles={{
this.props.displayPinnedManager(true); width: 'auto',
return false; height: 'auto',
}}>&nbsp;Pinned File Manager...&nbsp;</Button> : null} marginLeft: 'auto',
<div style={{marginBottom: '4px'}}/> marginRight: '4px',
}}
clicked={() => {
this.props.displayPinnedManager(true);
return false;
}}
>
&nbsp;Pinned File Manager...&nbsp;
</Button>
) : null}
<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

@@ -29,7 +29,7 @@ textarea.ConfigurationItemInput {
box-sizing: border-box; box-sizing: border-box;
resize: none; resize: none;
overflow-y: scroll; overflow-y: scroll;
overflow:-moz-scrollbars-horizontal; overflow: -moz-scrollbars-horizontal;
white-space: nowrap; white-space: nowrap;
} }

View File

@@ -1,31 +1,33 @@
import React from 'react'; import React from 'react';
import './ConfigurationItem.css'; import './ConfigurationItem.css';
import CheckBox from '../../../components/UI/CheckBox/CheckBox'; 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,137 +39,186 @@ export default connect(null, mapDispatchToProps)(props => {
props.notifyInfo(props.label, description); props.notifyInfo(props.label, description);
}; };
infoDisplay = <a href={'#'} infoDisplay = (
className={'ConfigurationInfo'} <a
onClick={()=>{displayInfo(); return false;}}><FontAwesomeIcon icon={faInfoCircle}/></a>; href={'#'}
className={'ConfigurationInfo'}
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 = (
checked={props.value} <CheckBox
disabled={props.readOnly} changed={handleChanged}
autoFocus={props.autoFocus}/>; checked={props.value}
break; disabled={props.readOnly}
autoFocus={props.autoFocus}
/>
);
break;
case 'double': case 'double':
data = <input min={0.0} data = (
autoFocus={props.autoFocus} <input
disabled={props.readOnly} min={0.0}
onChange={e=>handleChanged(e)} autoFocus={props.autoFocus}
step={'0.01'} disabled={props.readOnly}
className={'ConfigurationItemInput'} onChange={(e) => handleChanged(e)}
type={'number'} step={'0.01'}
value={parseFloat(props.value).toFixed(2)}/>; className={'ConfigurationItemInput'}
break; type={'number'}
value={parseFloat(props.value).toFixed(2)}
/>
);
break;
case 'list': case 'list':
data = <DropDown alt data = (
auto <DropDown
autoFocus={props.autoFocus} alt
changed={handleChanged} auto
disabled={props.readOnly} autoFocus={props.autoFocus}
items={props.items} changed={handleChanged}
selected={props.value} />; disabled={props.readOnly}
break; items={props.items}
selected={props.value}
/>
);
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}
target: { changed={(s) =>
type: 'password', handleChanged({
value: s, target: {
}, type: 'password',
})} value: s,
disabled={props.readOnly} },
mismatchHandler={() => props.notifyError('Passwords do not match')} })
value={props.value} /> }
disabled={props.readOnly}
mismatchHandler={() => props.notifyError('Passwords do not match')}
value={props.value}
/>
); );
} else { } else {
data = ( data = (
<input onChange={e => handleChanged(e)} <input
autoFocus={props.autoFocus} onChange={(e) => handleChanged(e)}
className={'ConfigurationItemInput'} autoFocus={props.autoFocus}
disabled={props.readOnly} className={'ConfigurationItemInput'}
type={'text'} disabled={props.readOnly}
value={props.value}/> type={'text'}
value={props.value}
/>
); );
} }
break; break;
case 'uint8': case 'uint8':
data = <input max={255} data = (
min={0} <input
autoFocus={props.autoFocus} max={255}
disabled={props.readOnly} min={0}
onChange={e=>handleChanged(e)} autoFocus={props.autoFocus}
className={'ConfigurationItemInput'} disabled={props.readOnly}
type={'number'} onChange={(e) => handleChanged(e)}
value={props.value}/>; className={'ConfigurationItemInput'}
break; type={'number'}
value={props.value}
/>
);
break;
case 'uint16': case 'uint16':
data = <input max={65535} data = (
min={0} <input
autoFocus={props.autoFocus} max={65535}
disabled={props.readOnly} min={0}
onChange={e=>handleChanged(e)} autoFocus={props.autoFocus}
className={'ConfigurationItemInput'} disabled={props.readOnly}
type={'number'} onChange={(e) => handleChanged(e)}
value={props.value}/>; className={'ConfigurationItemInput'}
break; type={'number'}
value={props.value}
/>
);
break;
case 'uint32': case 'uint32':
data = <input max={4294967295} data = (
min={0} <input
autoFocus={props.autoFocus} max={4294967295}
disabled={props.readOnly} min={0}
onChange={e=>handleChanged(e)} autoFocus={props.autoFocus}
className={'ConfigurationItemInput'} disabled={props.readOnly}
type={'number'} onChange={(e) => handleChanged(e)}
value={props.value}/>; className={'ConfigurationItemInput'}
break; type={'number'}
value={props.value}
/>
);
break;
case 'uint64': case 'uint64':
data = <input max={18446744073709551615} data = (
min={0} <input
autoFocus={props.autoFocus} max={18446744073709551615}
disabled={props.readOnly} min={0}
onChange={e=>handleChanged(e)} autoFocus={props.autoFocus}
className={'ConfigurationItemInput'} disabled={props.readOnly}
type={'number'} onChange={(e) => handleChanged(e)}
value={props.value}/>; className={'ConfigurationItemInput'}
break; type={'number'}
value={props.value}
/>
);
break;
case 'string_array': case 'string_array':
data = ( data = (
<textarea autoFocus={props.autoFocus} <textarea
disabled={props.readOnly} autoFocus={props.autoFocus}
rows={4} disabled={props.readOnly}
cols={36} rows={4}
onChange={e=>handleChanged(e)} cols={36}
className={'ConfigurationItemInput'} onChange={(e) => handleChanged(e)}
value={props.value.join('\n')} /> className={'ConfigurationItemInput'}
value={props.value.join('\n')}
/>
); );
break; break;
case 'password': case 'password':
data = ( data = (
<Password autoFocus={props.autoFocus} <Password
changed={s => handleChanged({ autoFocus={props.autoFocus}
target: { changed={(s) =>
type: 'password', handleChanged({
value: s, target: {
}, type: 'password',
})} value: s,
disabled={props.readOnly} },
mismatchHandler={() => props.notifyError('Passwords do not match')} })
value={props.value} /> }
disabled={props.readOnly}
mismatchHandler={() => props.notifyError('Passwords do not match')}
value={props.value}
/>
); );
break; break;
default: default:
data = <div>{props.value}</div>; data = <div>{props.value}</div>;
@@ -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

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import './MountItem.css'; import './MountItem.css';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import DropDown from '../../../components/UI/DropDown/DropDown'; import DropDown from '../../../components/UI/DropDown/DropDown';
import Button from '../../../components/UI/Button/Button'; import Button from '../../../components/UI/Button/Button';
import Loader from 'react-loader-spinner'; import Loader from 'react-loader-spinner';
@@ -11,36 +11,41 @@ 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';
const mapStateToProps = (state, ownProps) => { 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,
@@ -57,18 +62,28 @@ 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={
e.preventDefault(); props.MState.AllowMount
}} ? () =>
src={configureImage} props.displayConfiguration(
style={{padding: 0, border: 0, margin: 0, ...pointer}} props.provider,
width={'16px'}/> props.remote,
props.s3
)
: (e) => {
e.preventDefault();
}
}
src={configureImage}
style={{ padding: 0, border: 0, margin: 0, ...pointer }}
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 = (
colSpan={inputColumnSpan} <DropDown
disabled={!props.MState.AllowMount || props.MState.Mounted} changed={props.changed}
items={props.MState.DriveLetters} colSpan={inputColumnSpan}
row={secondRow} disabled={!props.MState.AllowMount || props.MState.Mounted}
rowSpan={7} items={props.MState.DriveLetters}
selected={index >= 0 ? props.PState.MountLocation : ''}/>; row={secondRow}
rowSpan={7}
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
key={'i' + key++} colSpan={inputColumnSpan - 8}
row={secondRow} key={'i' + key++}
rowSpan={7}> row={secondRow}
<input disabled={!props.MState.AllowMount || props.MState.Mounted} rowSpan={7}
maxLength={4096} >
onChange={props.changed} <input
size={4096} disabled={!props.MState.AllowMount || props.MState.Mounted}
className={'MountItemInput'} maxLength={4096}
type={'text'} onChange={props.changed}
value={props.PState.MountLocation}/> size={4096}
className={'MountItemInput'}
type={'text'}
value={props.PState.MountLocation}
/>
</RootElem> </RootElem>
)); );
inputControls.push(( inputControls.push(
<Button clicked={()=>props.browseClicked(props.provider, props.PState.MountLocation)} <Button
col={inputColumnSpan - 7} clicked={() =>
colSpan={7} props.browseClicked(props.provider, props.PState.MountLocation)
disabled={props.MState.Mounted || !props.MState.AllowMount} }
key={'b' + key++} col={inputColumnSpan - 7}
row={secondRow} colSpan={7}
rowSpan={7}>...</Button> disabled={props.MState.Mounted || !props.MState.AllowMount}
)); key={'b' + key++}
row={secondRow}
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'
height={19} ) : (
type='Circles' 'Mount'
width={19}/>; )
) : (
<Loader
color={'var(--heading_text_color)'}
height={19}
type="Circles"
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
colSpan={28} col={inputColumnSpan + 24}
row={secondRow} colSpan={28}
rowSpan={7}> row={secondRow}
<CheckBox changed={handleAutoMountChanged} rowSpan={7}
checked={props.PState.AutoMount} >
label={'Auto-mount'}/> <CheckBox
changed={handleAutoMountChanged}
checked={props.PState.AutoMount}
label={'Auto-mount'}
/>
</RootElem> </RootElem>
); );
const autoRestartControl = ( const autoRestartControl = (
<RootElem col={inputColumnSpan + 24 + 28} <RootElem
colSpan={24} col={inputColumnSpan + 24 + 28}
row={secondRow} colSpan={24}
rowSpan={7}> row={secondRow}
<CheckBox changed={handleAutoRestartChanged} rowSpan={7}
checked={props.PState.AutoRestart} >
label={'Restart'}/> <CheckBox
changed={handleAutoRestartChanged}
checked={props.PState.AutoRestart}
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 ? (
col={(configButton ? 24 : 18) + 34} <a
onClick={props.MState.AllowMount ? () => props.displaySkynetExport(true) : e => { href={'#'}
e.preventDefault(); col={(configButton ? 24 : 18) + 34}
}} onClick={
rowSpan={5} props.MState.AllowMount
style={{...pointer, fontWeight: 'normal'}}> ? () => props.displaySkynetExport(true)
: (e) => {
e.preventDefault();
}
}
rowSpan={5}
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
col={(configButton ? 24 + 13 : 18 + 13) + 34} href={'#'}
onClick={props.MState.AllowMount ? () => props.displaySkynetImport(true) : e => { col={(configButton ? 24 + 13 : 18 + 13) + 34}
e.preventDefault(); onClick={
}} props.MState.AllowMount
rowSpan={5} ? () => props.displaySkynetImport(true)
style={{...pointer, fontWeight: 'normal'}}> : (e) => {
e.preventDefault();
}
}
rowSpan={5}
style={{ ...pointer, fontWeight: 'normal' }}
>
<u>Import</u> <u>Import</u>
</a> </a>
) : null} ) : null}

View File

@@ -2,7 +2,7 @@ import React from 'react';
import AddMount from '../AddMount/AddMount'; import AddMount from '../AddMount/AddMount';
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 {connect} from 'react-redux'; import { connect } from 'react-redux';
import './MountItems.css'; import './MountItems.css';
import Modal from '../../components/UI/Modal/Modal'; import Modal from '../../components/UI/Modal/Modal';
import MountItem from './MountItem/MountItem'; import MountItem from './MountItem/MountItem';
@@ -14,9 +14,9 @@ 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';
const Constants = require('../../constants'); const Constants = require('../../constants');
@@ -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, {
RetryItems: retryItems, DisplayRetry: Object.keys(retryItems).length > 0,
}, () => { RetryItems: retryItems,
if (this.state.DisplayRetry) { },
this.sendRequest(Constants.IPC_Show_Window); () => {
if (this.state.DisplayRetry) {
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, {
@@ -86,10 +98,10 @@ 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,28 +122,37 @@ class MountItems extends IPCContainer {
}; };
this.props.setMountState(provider, mountState); this.props.setMountState(provider, mountState);
this.setState({ this.setState(
DisplayRetry: true, {
RetryItems: retryItems, DisplayRetry: true,
}, () => { RetryItems: retryItems,
this.sendRequest(Constants.IPC_Show_Window); },
this.retryIntervals[provider] = setInterval(() => { () => {
let retryItems = { this.sendRequest(Constants.IPC_Show_Window);
...this.state.RetryItems, this.retryIntervals[provider] = setInterval(() => {
}; let retryItems = {
const retrySeconds = retryItems[provider].RetrySeconds - 1; ...this.state.RetryItems,
if (retrySeconds === 0) { };
this.cancelRetryMount(provider, () => { const retrySeconds = retryItems[provider].RetrySeconds - 1;
this.handleMountUnMount(provider, remote, s3, true, mountLocation); if (retrySeconds === 0) {
}); this.cancelRetryMount(provider, () => {
} else { this.handleMountUnMount(
retryItems[provider].RetrySeconds = retrySeconds; provider,
this.setState({ remote,
RetryItems: retryItems, s3,
}); true,
} mountLocation
}, 1000); );
}); });
} else {
retryItems[provider].RetrySeconds = retrySeconds;
this.setState({
RetryItems: retryItems,
});
}
}, 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 }
Provider: provider, : this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
Remote: remote, Provider: provider,
S3: s3, Remote: remote,
Version: this.props.InstalledVersion S3: s3,
}).data; Version: this.props.InstalledVersion,
}).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(
Location: location, Constants.IPC_Check_Mount_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,104 +419,162 @@ 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
textAlign: 'center', dxStyle={{ padding: 'var(--default_spacing)', minWidth: '70vw' }}
paddingBottom: 'var(--default_spacing)', >
color: 'var(--text_color_error)' <h1
}}>Mount Failed</h1> style={{
textAlign: 'center',
paddingBottom: 'var(--default_spacing)',
color: 'var(--text_color_error)',
}}
>
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(
s3Supported={this.props.s3Supported} <AddMount
key={'hi_' + footerItems.length}/>); remoteSupported={this.props.remoteSupported}
s3Supported={this.props.s3Supported}
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
browseClicked={this.handleBrowseLocation} allowRemove={false}
changed={e => this.handleMountLocationChanged(provider, e.target.value)} browseClicked={this.handleBrowseLocation}
clicked={this.handleMountUnMount} changed={(e) =>
key={'it_' + items.length} this.handleMountLocationChanged(provider, e.target.value)
provider={provider}/> }
)); clicked={this.handleMountUnMount}
items.push(<div key={'it_' + items.length} key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}} />) provider={provider}
/>
);
items.push(
<div
key={'it_' + items.length}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
} }
if (this.props.remoteSupported) { 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
browseClicked={this.handleBrowseLocation} allowRemove={true}
changed={e => this.handleMountLocationChanged(provider, e.target.value)} browseClicked={this.handleBrowseLocation}
clicked={this.handleMountUnMount} changed={(e) =>
key={'it_' + items.length} this.handleMountLocationChanged(provider, e.target.value)
provider={provider} }
remote/> clicked={this.handleMountUnMount}
)); key={'it_' + items.length}
items.push(<div key={'it_' + items.length} provider={provider}
style={{paddingTop: 'var(--default_spacing)'}}/>) remote
/>
);
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
browseClicked={this.handleBrowseLocation} allowRemove={true}
changed={e => this.handleMountLocationChanged(provider, e.target.value)} browseClicked={this.handleBrowseLocation}
clicked={this.handleMountUnMount} changed={(e) =>
key={'it_' + items.length} this.handleMountLocationChanged(provider, e.target.value)
provider={provider} }
s3/> clicked={this.handleMountUnMount}
)); key={'it_' + items.length}
items.push(<div key={'it_' + items.length} provider={provider}
style={{paddingTop: 'var(--default_spacing)'}}/>) s3
/>
);
items.push(
<div
key={'it_' + items.length}
style={{ paddingTop: 'var(--default_spacing)' }}
/>
);
} }
} }
items.splice(items.length - 1, 1); items.splice(items.length - 1, 1);
return ( return (
<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

@@ -1,180 +1,227 @@
import React from 'react'; import React from 'react';
import './PinnedManager.css'; import './PinnedManager.css';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
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 {notifyError, notifyInfo} from '../../redux/actions/error_actions'; import { notifyError, notifyInfo } from '../../redux/actions/error_actions';
import Box from '../../components/UI/Box/Box'; import Box from '../../components/UI/Box/Box';
import {displayPinnedManager} from '../../redux/actions/pinned_manager_actions'; import { displayPinnedManager } from '../../redux/actions/pinned_manager_actions';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {faFolder} from '@fortawesome/free-solid-svg-icons'; import { faFolder } from '@fortawesome/free-solid-svg-icons';
import Button from '../../components/UI/Button/Button'; import Button from '../../components/UI/Button/Button';
import CheckBox from '../../components/UI/CheckBox/CheckBox'; 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(
state = { mapStateToProps,
active_directory: '/', mapDispatchToProps
items: [], )(
previous: [], class extends IPCContainer {
} state = {
active_directory: '/',
items: [],
previous: [],
};
componentDidMount() { componentDidMount() {
this.setRequestHandler(Constants.IPC_Get_Directory_Items_Reply, this.onGetDirectoryItemsReply); this.setRequestHandler(
this.grabDirectoryItems(); Constants.IPC_Get_Directory_Items_Reply,
} this.onGetDirectoryItemsReply
);
componentWillUnmount() { this.grabDirectoryItems();
super.componentWillUnmount();
}
grabDirectoryItems = () => {
this.sendRequest(Constants.IPC_Get_Directory_Items, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
S3: this.props.DisplayS3Configuration,
Version: this.props.version,
Path: this.state.active_directory,
});
}
onGetDirectoryItemsReply = (_, {data}) => {
if (data.Success) {
const items = data.Items
.filter(i => i.path !== '.' && (this.state.active_directory !== '/' || (i.path.substr(0, 1) !== '.')))
.map(i => {
return {
...i,
name: i.path === '..' ? i.path : i.path.substr(i.path.lastIndexOf('/') + 1),
meta: {
...i.meta,
pinned: i.meta.pinned === '1',
}
}
});
this.setState({
items,
});
} else {
this.props.notifyError(data.Error, () => {
this.props.displayPinnedManager(false);
});
} }
}
createDirectory = (name, path, idx, total, item_idx) => { componentWillUnmount() {
const style = {} super.componentWillUnmount();
if (item_idx + 1 !== total) {
style.marginBottom = '4px';
} }
return (
<div key={'dir_' + idx} style={{...style}}>
<Button buttonStyles={{textAlign: 'left'}}
clicked={() => {
const previous = [...this.state.previous];
if (path === '..') {
path = previous.pop();
} else {
previous.push(this.state.active_directory);
}
this.setState({
items: [],
active_directory: path,
previous,
}, () => {
this.grabDirectoryItems();
});
}}>
<FontAwesomeIcon icon={faFolder}
fixedWidth
color={'var(--heading_text_color)'}
style={{padding: 0, margin: 0}}/>
&nbsp;{name}
</Button>
</div>
);
}
createFile = (name, path, pinned, idx, total, item_idx) => { grabDirectoryItems = () => {
const style = {textAlign: 'left'} this.sendRequest(Constants.IPC_Get_Directory_Items, {
if (item_idx + 1 !== total) { Provider: this.props.DisplayConfiguration,
style.marginBottom = '2px'; Remote: this.props.DisplayRemoteConfiguration,
} S3: this.props.DisplayS3Configuration,
return ( Version: this.props.version,
<div key={'file_' + idx} style={{...style}}> Path: this.state.active_directory,
<CheckBox checked={pinned} });
changed={() => { };
const items = JSON.parse(JSON.stringify(this.state.items));
const pinned = items[item_idx].meta.pinned = !items[item_idx].meta.pinned;
this.setState({
items
}, () => {
this.sendSyncRequest(Constants.IPC_Set_Pinned, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
S3: this.props.DisplayS3Configuration,
Version: this.props.version,
Path: path,
Pinned: pinned,
});
});
}}
label={name}/>
</div>
);
}
render() { onGetDirectoryItemsReply = (_, { data }) => {
let idx = 0; if (data.Success) {
return ( const items = data.Items.filter(
<Box dxDark dxStyle={{ (i) =>
height: 'calc(100vh - (var(--default_spacing) * 4)', i.path !== '.' &&
padding: 'var(--default_spacing)', (this.state.active_directory !== '/' || i.path.substr(0, 1) !== '.')
width: 'calc(100vw - (var(--default_spacing) * 4)' ).map((i) => {
}}> return {
<div className={'PinnedManager'}> ...i,
<div className={'PinnedManagerHeading'}> name:
<div className={'PinnedManagerClose'}> i.path === '..'
<a href={'#'} ? i.path
onClick={() => this.props.displayPinnedManager(false)} : i.path.substr(i.path.lastIndexOf('/') + 1),
style={{cursor: 'pointer', flex: '0'}}>X</a> meta: {
</div> ...i.meta,
<h1 style={{width: '100%', textAlign: 'center'}}>{'Pinned File Manager'}</h1> pinned: i.meta.pinned === '1',
<div className={'PinnedManagerActiveDirectory'}> },
<b>&nbsp;{this.state.active_directory}</b> };
</div> });
</div> this.setState({
<div className={'PinnedManagerItemsOwner'}> items,
<div className={'PinnedManagerItems'}> });
{ } else {
this.state.items.map((i, k) => { this.props.notifyError(data.Error, () => {
return i.directory ? this.props.displayPinnedManager(false);
this.createDirectory(i.name, i.path, idx++, this.state.items.length, k) : });
this.createFile(i.name, i.path, i.meta.pinned, idx++, this.state.items.length, k); }
}) };
createDirectory = (name, path, idx, total, item_idx) => {
const style = {};
if (item_idx + 1 !== total) {
style.marginBottom = '4px';
}
return (
<div key={'dir_' + idx} style={{ ...style }}>
<Button
buttonStyles={{ textAlign: 'left' }}
clicked={() => {
const previous = [...this.state.previous];
if (path === '..') {
path = previous.pop();
} else {
previous.push(this.state.active_directory);
} }
this.setState(
{
items: [],
active_directory: path,
previous,
},
() => {
this.grabDirectoryItems();
}
);
}}
>
<FontAwesomeIcon
icon={faFolder}
fixedWidth
color={'var(--heading_text_color)'}
style={{ padding: 0, margin: 0 }}
/>
&nbsp;{name}
</Button>
</div>
);
};
createFile = (name, path, pinned, idx, total, item_idx) => {
const style = { textAlign: 'left' };
if (item_idx + 1 !== total) {
style.marginBottom = '2px';
}
return (
<div key={'file_' + idx} style={{ ...style }}>
<CheckBox
checked={pinned}
changed={() => {
const items = JSON.parse(JSON.stringify(this.state.items));
const pinned = (items[item_idx].meta.pinned = !items[item_idx]
.meta.pinned);
this.setState(
{
items,
},
() => {
this.sendSyncRequest(Constants.IPC_Set_Pinned, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
S3: this.props.DisplayS3Configuration,
Version: this.props.version,
Path: path,
Pinned: pinned,
});
}
);
}}
label={name}
/>
</div>
);
};
render() {
let idx = 0;
return (
<Box
dxDark
dxStyle={{
height: 'calc(100vh - (var(--default_spacing) * 4)',
padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)',
}}
>
<div className={'PinnedManager'}>
<div className={'PinnedManagerHeading'}>
<div className={'PinnedManagerClose'}>
<a
href={'#'}
onClick={() => this.props.displayPinnedManager(false)}
style={{ cursor: 'pointer', flex: '0' }}
>
X
</a>
</div>
<h1 style={{ width: '100%', textAlign: 'center' }}>
{'Pinned File Manager'}
</h1>
<div className={'PinnedManagerActiveDirectory'}>
<b>&nbsp;{this.state.active_directory}</b>
</div>
</div>
<div className={'PinnedManagerItemsOwner'}>
<div className={'PinnedManagerItems'}>
{this.state.items.map((i, k) => {
return i.directory
? this.createDirectory(
i.name,
i.path,
idx++,
this.state.items.length,
k
)
: this.createFile(
i.name,
i.path,
i.meta.pinned,
idx++,
this.state.items.length,
k
);
})}
</div>
</div> </div>
</div> </div>
</div> </Box>
</Box> );
) }
} }
}); );

View File

@@ -1,43 +1,50 @@
import React from 'react'; import React from 'react';
import './SelectAppPlatform.css'; import './SelectAppPlatform.css';
import axios from 'axios'; import axios from 'axios';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
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 { 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';
import {notifyError} from '../../redux/actions/error_actions'; import { notifyError } from '../../redux/actions/error_actions';
import {setInstallTestActive} from '../../redux/actions/install_actions'; import { setInstallTestActive } from '../../redux/actions/install_actions';
class SelectAppPlatform extends IPCContainer { class SelectAppPlatform extends IPCContainer {
state = { state = {
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),
@@ -57,36 +66,48 @@ class SelectAppPlatform extends IPCContainer {
render() { render() {
return ( return (
<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
disabled={this.props.InstallTestActive} changed={this.handleChanged}
items={Constants.LINUX_SELECTABLE_PLATFORMS} disabled={this.props.InstallTestActive}
selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}/> items={Constants.LINUX_SELECTABLE_PLATFORMS}
<Button clicked={this.handleTestClicked} selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}
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

@@ -1,59 +1,78 @@
import React from 'react'; import React from 'react';
import './SkynetExport.css'; import './SkynetExport.css';
import CheckboxTree from 'react-checkbox-tree'; import CheckboxTree from 'react-checkbox-tree';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
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 {notifyError, notifyInfo} from '../../redux/actions/error_actions'; import { notifyError, notifyInfo } from '../../redux/actions/error_actions';
import Box from '../../components/UI/Box/Box'; 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(
state = { mapStateToProps,
checked: [], mapDispatchToProps
clicked: {}, )(
expanded: [], class extends IPCContainer {
nodes: [], state = {
second_stage: false, checked: [],
} clicked: {},
expanded: [],
nodes: [],
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: '',
path: '', path: '',
@@ -61,175 +80,269 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCCon
children: [], children: [],
} }
*/ */
const ret = []; const ret = [];
for (const item of items) { for (const item of items) {
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) {
treeItem.children = this.createNodes(item.children); treeItem.children = this.createNodes(item.children);
}
ret.push(treeItem);
} }
ret.push(treeItem); return ret;
} };
return ret; handleNavigation = () => {
} if (this.state.second_stage) {
this.props.notifyApplicationBusy(true);
handleNavigation = () => { this.sendRequest(Constants.IPC_Export_Skylinks, {
if (this.state.second_stage) { Version: this.props.version,
this.props.notifyApplicationBusy(true); Paths: this.state.checked,
this.sendRequest(Constants.IPC_Export_Skylinks, {
Version: this.props.version,
Paths: this.state.checked,
});
} else {
if (this.state.checked.length === 0) {
this.props.notifyError('No files have been checked');
} else {
this.setState({
second_stage: true,
}); });
} else {
if (this.state.checked.length === 0) {
this.props.notifyError('No files have been checked');
} else {
this.setState({
second_stage: true,
});
}
} }
} };
}
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: [],
clicked: {},
expanded: [],
nodes: [],
second_stage: false,
}, () => {
this.props.notifyInfo('Skylink Exports', '!alternate!!copyable!' + JSON.stringify(arg.data.Result.success, null, 2));
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version});
});
} else {
this.props.notifyError(arg.data.Error);
}
}
onGrabSkynetTreeReply = (_, arg) => {
if (arg.data.Success) {
this.setState({
checked: [],
expanded: [0],
nodes: this.createNodes(arg.data.Result),
});
} else {
this.setState({
checked: [],
expanded: [],
nodes: [],
}, () => {
this.props.notifyError(arg.data.Error);
});
}
}
render() {
return this.props.AppBusy ? (<div/>) : (
<Box dxDark dxStyle={{
height: '90vh',
padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)'
}}>
<div
style={{
float: 'right',
margin: 0,
padding: 0,
marginTop: '-4px',
boxSizing: 'border-box',
display: 'block'
}}>
<a href={'#'}
onClick={() => this.props.displaySkynetExport(false)}
style={{cursor: 'pointer'}}>X</a>
</div>
<h1
className={'SkynetExportHeading'}>{this.state.second_stage ? 'Verify Exports' : 'Export Files'}</h1>
<div className={this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'}>
{ {
this.state.second_stage ? checked: [],
this.state.checked.map(path => { clicked: {},
expanded: [],
nodes: [],
second_stage: false,
},
() => {
this.props.notifyInfo(
'Skylink Exports',
'!alternate!!copyable!' +
JSON.stringify(arg.data.Result.success, null, 2)
);
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {
Version: this.props.version,
});
}
);
} else {
this.props.notifyError(arg.data.Error);
}
};
onGrabSkynetTreeReply = (_, arg) => {
if (arg.data.Success) {
this.setState({
checked: [],
expanded: [0],
nodes: this.createNodes(arg.data.Result),
});
} else {
this.setState(
{
checked: [],
expanded: [],
nodes: [],
},
() => {
this.props.notifyError(arg.data.Error);
}
);
}
};
render() {
return this.props.AppBusy ? (
<div />
) : (
<Box
dxDark
dxStyle={{
height: '90vh',
padding: 'var(--default_spacing)',
width: 'calc(100vw - (var(--default_spacing) * 4)',
}}
>
<div
style={{
float: 'right',
margin: 0,
padding: 0,
marginTop: '-4px',
boxSizing: 'border-box',
display: 'block',
}}
>
<a
href={'#'}
onClick={() => this.props.displaySkynetExport(false)}
style={{ cursor: 'pointer' }}
>
X
</a>
</div>
<h1 className={'SkynetExportHeading'}>
{this.state.second_stage ? 'Verify Exports' : 'Export Files'}
</h1>
<div
className={
this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'
}
>
{this.state.second_stage ? (
this.state.checked.map((path) => {
return ( return (
<input readOnly <input
className={'ConfigurationItemInput'} readOnly
key={path} className={'ConfigurationItemInput'}
style={{width: '100%', marginBottom: 'var(--default_spacing)'}} key={path}
type={'text'} style={{
value={path}/> width: '100%',
marginBottom: 'var(--default_spacing)',
}}
type={'text'}
value={path}
/>
); );
}) })
: ( ) : (
<CheckboxTree checked={this.state.checked} <CheckboxTree
expanded={this.state.expanded} checked={this.state.checked}
expandOnClick expanded={this.state.expanded}
showExpandAll expandOnClick
icons={{ showExpandAll
check: <FontAwesomeIcon icon={faCheckSquare} fixedWidth icons={{
style={{padding: 0, margin: 0}}/>, check: (
uncheck: <FontAwesomeIcon icon={faSquare} fixedWidth <FontAwesomeIcon
style={{padding: 0, margin: 0}}/>, icon={faCheckSquare}
halfCheck: <FontAwesomeIcon icon={faHSquare} fixedWidth fixedWidth
style={{padding: 0, margin: 0}}/>, style={{ padding: 0, margin: 0 }}
expandClose: <FontAwesomeIcon icon={faChevronRight} fixedWidth />
style={{padding: 0, margin: 0}}/>, ),
expandOpen: <FontAwesomeIcon icon={faChevronDown} fixedWidth uncheck: (
style={{padding: 0, margin: 0}}/>, <FontAwesomeIcon
expandAll: <FontAwesomeIcon icon={faPlusSquare} fixedWidth icon={faSquare}
style={{padding: 0, margin: 0}}/>, fixedWidth
collapseAll: <FontAwesomeIcon icon={faMinusSquare} fixedWidth style={{ padding: 0, margin: 0 }}
style={{padding: 0, margin: 0}}/>, />
parentClose: <FontAwesomeIcon icon={faFolder} ),
fixedWidth halfCheck: (
color={'var(--heading_text_color)'} <FontAwesomeIcon
style={{padding: 0, margin: 0}}/>, icon={faHSquare}
parentOpen: <FontAwesomeIcon icon={faFolderOpen} fixedWidth
fixedWidth style={{ padding: 0, margin: 0 }}
color={'var(--heading_text_color)'} />
style={{padding: 0, margin: 0}}/>, ),
leaf: <FontAwesomeIcon icon={faFile} expandClose: (
fixedWidth <FontAwesomeIcon
color={'var(--text_color)'} icon={faChevronRight}
style={{padding: 0, margin: 0}}/> fixedWidth
}} style={{ padding: 0, margin: 0 }}
nodes={this.state.nodes} />
onClick={clicked => this.setState({clicked})} ),
onCheck={checked => this.setState({checked})} expandOpen: (
onExpand={expanded => this.setState({expanded})}/> <FontAwesomeIcon
) icon={faChevronDown}
} fixedWidth
</div> style={{ padding: 0, margin: 0 }}
<div style={{display: 'flex', justifyContent: 'flex-end'}}> />
{ ),
this.state.second_stage ? expandAll: (
<Button buttonStyles={{ <FontAwesomeIcon
icon={faPlusSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
collapseAll: (
<FontAwesomeIcon
icon={faMinusSquare}
fixedWidth
style={{ padding: 0, margin: 0 }}
/>
),
parentClose: (
<FontAwesomeIcon
icon={faFolder}
fixedWidth
color={'var(--heading_text_color)'}
style={{ padding: 0, margin: 0 }}
/>
),
parentOpen: (
<FontAwesomeIcon
icon={faFolderOpen}
fixedWidth
color={'var(--heading_text_color)'}
style={{ padding: 0, margin: 0 }}
/>
),
leaf: (
<FontAwesomeIcon
icon={faFile}
fixedWidth
color={'var(--text_color)'}
style={{ padding: 0, margin: 0 }}
/>
),
}}
nodes={this.state.nodes}
onClick={(clicked) => this.setState({ clicked })}
onCheck={(checked) => this.setState({ checked })}
onExpand={(expanded) => this.setState({ expanded })}
/>
)}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
{this.state.second_stage ? (
<Button
buttonStyles={{
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', 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, clicked={this.handleNavigation}
})}>{'Back'}</Button> : >
null {this.state.second_stage ? 'Export' : 'Next'}
} </Button>
<Button buttonStyles={{ </div>
height: 'auto', </Box>
marginLeft: 'var(--default_spacing)', );
marginTop: 'var(--default_spacing)', }
width: 'auto'
}}
clicked={this.handleNavigation}>{this.state.second_stage ? 'Export' : 'Next'}</Button>
</div>
</Box>
);
} }
}); );

View File

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

View File

@@ -1,29 +1,38 @@
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 }) => {
let key = 0; let key = 0;
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,228 +1,278 @@
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 = {
import_text: '',
imports_array: [],
second_stage: false,
};
state = { componentDidMount() {
import_text: '', this.setRequestHandler(
imports_array: [], Constants.IPC_Import_Skylinks_Reply,
second_stage: false, this.onImportSkylinksReply
}; );
}
componentDidMount() { componentWillUnmount() {
this.setRequestHandler(Constants.IPC_Import_Skylinks_Reply, this.onImportSkylinksReply); super.componentWillUnmount();
} }
componentWillUnmount() { displaySyntax = () => {
super.componentWillUnmount(); const msg =
} '!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' +
' AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA\n' +
' AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg\n' +
'\n' +
'To import Skylinks into new or existing directories, use the following syntax:\n' +
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA"\n' +
' directory="/my/sub/dir2",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg"\n' +
'\n' +
'You can also specify a password if this in an encrypted Repertory Skylink:\n' +
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",token="My Password"\n' +
' directory="/my/sub/dir",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",token="My Password"\n' +
'\n' +
'To import Skylinks using JSON syntax:\n' +
' [\n' +
' {\n' +
' "directory": "/",\n' +
' "skylink": "AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",\n' +
' "token": "My Password"\n' +
' },\n' +
' {\n' +
' "directory": "/my/sub/dir",\n' +
' "skylink": "AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",\n' +
' "token": "My Password"\n' +
' }\n' +
' ]';
this.props.notifyInfo('Import Syntax', msg);
};
displaySyntax = () => { handleNavigation = () => {
const msg = '!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' + if (this.state.second_stage) {
' AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA\n' + try {
' AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg\n' + this.props.notifyApplicationBusy(true);
'\n' + this.sendRequest(Constants.IPC_Import_Skylinks, {
'To import Skylinks into new or existing directories, use the following syntax:\n' + Version: this.props.version,
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA"\n' + JsonArray: this.state.imports_array,
' directory="/my/sub/dir2",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg"\n' + });
'\n' + } catch (e) {
'You can also specify a password if this in an encrypted Repertory Skylink:\n' + this.props.notifyApplicationBusy(false);
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",token="My Password"\n' + this.props.notifyError(e);
' directory="/my/sub/dir",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",token="My Password"\n' + }
'\n' + } else {
'To import Skylinks using JSON syntax:\n' + const items = this.state.import_text.split('\n');
' [\n' + let importsArray = [];
' {\n' + try {
' "directory": "/",\n' + for (let item of items) {
' "skylink": "AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",\n' + item = item.trim();
' "token": "My Password"\n' + if (item.startsWith('[')) {
' },\n' + importsArray = JSON.parse(this.state.import_text.trim());
' {\n' + break;
' "directory": "/my/sub/dir",\n' + } else if (item.indexOf('=') >= 0) {
' "skylink": "AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",\n' + const parts = item.split(',');
' "token": "My Password"\n' + let importItem = {
' }\n' + directory: '/',
' ]'; skylink: '',
this.props.notifyInfo('Import Syntax', msg) token: '',
}
handleNavigation = () => {
if (this.state.second_stage) {
try {
this.props.notifyApplicationBusy(true);
this.sendRequest(Constants.IPC_Import_Skylinks, {
Version: this.props.version,
JsonArray: this.state.imports_array,
});
} catch (e) {
this.props.notifyApplicationBusy(false);
this.props.notifyError(e);
}
} else {
const items = this.state.import_text.split('\n');
let importsArray = [];
try {
for (let item of items) {
item = item.trim();
if (item.startsWith('[')) {
importsArray = JSON.parse(this.state.import_text.trim());
break;
} else if (item.indexOf('=') >= 0) {
const parts = item.split(',')
let importItem = {
directory: '/',
skylink: '',
token: '',
};
for (let part of parts) {
part = part.trim();
const pair = part.split('=');
if (pair.length !== 2) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
}
importItem = {
...importItem,
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''),
}; };
for (let part of parts) {
part = part.trim();
const pair = part.split('=');
if (pair.length !== 2) {
throw new Error(
'Invalid syntax for import: directory="",skylink="",token=""'
);
}
importItem = {
...importItem,
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''),
};
}
if (!importItem.skylink) {
throw new Error(
'Invalid syntax for import: directory="",skylink="",token=""'
);
}
importsArray.push(importItem);
} else if (item.length > 0) {
importsArray.push({
directory: '/',
skylink: item,
});
} }
if (!importItem.skylink) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
}
importsArray.push(importItem);
} else if (item.length > 0) {
importsArray.push({
directory: '/',
skylink: item,
});
} }
}
if (importsArray.length === 0) { if (importsArray.length === 0) {
throw new Error('Nothing to import'); throw new Error('Nothing to import');
}
this.setState({
imports_array: importsArray,
second_stage: true,
});
} catch (e) {
this.props.notifyError(e);
} }
this.setState({
imports_array: importsArray,
second_stage: true,
});
} catch (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) : '', {
imports_array: [], import_text:
second_stage: false, failedImportsArray.length > 0
}, () => { ? JSON.stringify(failedImportsArray, null, 2)
if (failedImportsArray.length > 0) { : '',
this.props.notifyError(`Failed to import ${failedImportsArray.length} item(s)`); imports_array: [],
} else { second_stage: false,
this.props.notifyInfo('Import Result', `Successfully imported ${count} item(s)`); },
} () => {
}); if (failedImportsArray.length > 0) {
} else { this.props.notifyError(
this.props.notifyError(arg.data.Error); `Failed to import ${failedImportsArray.length} item(s)`
} );
}; } else {
this.props.notifyInfo(
'Import Result',
`Successfully imported ${count} item(s)`
);
}
}
);
} else {
this.props.notifyError(arg.data.Error);
}
};
render() { render() {
return this.props.AppBusy ? (<div/>) : ( return this.props.AppBusy ? (
<Box dxDark dxStyle={{ <div />
height: 'auto', ) : (
padding: 'var(--default_spacing)', <Box
width: 'calc(100vw - (var(--default_spacing) * 4)' dxDark
}}> dxStyle={{
<div height: 'auto',
style={{ padding: 'var(--default_spacing)',
float: 'right', width: 'calc(100vw - (var(--default_spacing) * 4)',
margin: 0, }}
padding: 0, >
marginTop: '-4px', <div
boxSizing: 'border-box', style={{
display: 'block' float: 'right',
}}> margin: 0,
<a href={'#'} padding: 0,
onClick={() => this.props.displaySkynetImport(false)} marginTop: '-4px',
style={{cursor: 'pointer'}}>X</a> boxSizing: 'border-box',
</div> display: 'block',
<h1 }}
className={'SkynetImportHeading'}>{this.state.second_stage ? 'Verify Imports' : 'Import List'}</h1> >
{ <a
this.state.second_stage ? ( href={'#'}
<ImportList imports_array={this.state.imports_array}/> onClick={() => this.props.displaySkynetImport(false)}
style={{ cursor: 'pointer' }}
>
X
</a>
</div>
<h1 className={'SkynetImportHeading'}>
{this.state.second_stage ? 'Verify Imports' : 'Import List'}
</h1>
{this.state.second_stage ? (
<ImportList imports_array={this.state.imports_array} />
) : ( ) : (
<textarea autoFocus={true} <textarea
className={'SkynetImportTextArea'} autoFocus={true}
onChange={e => this.setState({ className={'SkynetImportTextArea'}
import_text: e.target.value, onChange={(e) =>
})} this.setState({
value={this.state.import_text} import_text: e.target.value,
rows={10}/> })
) }
} value={this.state.import_text}
<div className={'SkynetImportButtons'}> rows={10}
<Button />
buttonStyles={{height: 'auto', marginTop: 'var(--default_spacing)', width: 'auto'}} )}
clicked={this.displaySyntax}>Import Syntax...</Button> <div className={'SkynetImportButtons'}>
<div className={'SkynetActionButtons'}> <Button
{ buttonStyles={{
this.state.second_stage ? height: 'auto',
<Button buttonStyles={{ marginTop: 'var(--default_spacing)',
width: 'auto',
}}
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', 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, clicked={this.handleNavigation}
})}>{'Back'}</Button> : >
null {this.state.second_stage ? 'Import' : 'Next'}
} </Button>
<Button buttonStyles={{ </div>
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> </Box>
</Box> );
); }
} }
}); );

View File

@@ -1,7 +1,7 @@
import React, {Component} from 'react'; import React, { Component } from 'react';
import './Password.css'; import './Password.css';
import {faEye, faEyeSlash} from '@fortawesome/free-solid-svg-icons'; import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
export default class Password extends Component { export default class Password extends Component {
state = { state = {
@@ -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, {
button_text: 'set', ...this.state,
password: '', button_text: 'set',
password2: '', password: '',
}, () => { password2: '',
if (this.props.mismatchHandler) { },
this.props.mismatchHandler(); () => {
if (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();
} }
}; };
@@ -108,28 +117,41 @@ 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
className={'PasswordLink'} href={'#'}
onClick={this.handleActionClick}> className={'PasswordLink'}
onClick={this.handleActionClick}
>
<u>{this.state.button_text}</u> <u>{this.state.button_text}</u>
</a> </a>
} )}
<input autoFocus={this.props.autoFocus} <input
readOnly={this.props.readOnly} autoFocus={this.props.autoFocus}
className={'PasswordInput'} readOnly={this.props.readOnly}
disabled={this.props.readOnly || (this.state.button_text === 'clear')} className={'PasswordInput'}
onChange={this.handlePasswordChanged} disabled={this.props.readOnly || this.state.button_text === 'clear'}
onKeyUp={this.handlePasswordKeyUp} onChange={this.handlePasswordChanged}
type={this.state.show_password ? 'text' : 'password'} onKeyUp={this.handlePasswordKeyUp}
value={(this.state.button_text === 'confirm') ? this.state.password2 : this.state.password}/> type={this.state.show_password ? 'text' : 'password'}
<a href={'#'} value={
className={'PasswordShowHide'} this.state.button_text === 'confirm'
onClick={this.handleShowHideClick}> ? this.state.password2
<FontAwesomeIcon icon={this.state.show_password ? faEye : faEyeSlash} fixedWidth/> : this.state.password
}
/>
<a
href={'#'}
className={'PasswordShowHide'}
onClick={this.handleShowHideClick}
>
<FontAwesomeIcon
icon={this.state.show_password ? faEye : faEyeSlash}
fixedWidth
/>
</a> </a>
</div> </div>
); );
} }
}; }

File diff suppressed because it is too large Load Diff

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',
b64signature, })
Constants.DEV_PUBLIC_KEY); .replace(/(\r\n|\n|\r)/gm, '');
const data = helpers.createSignatureFiles(
b64signature,
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,12 +35,13 @@ a {
font-weight: bold; font-weight: bold;
} }
html, body { html,
height: 100%; body {
width: 100%; height: 100%;
margin: 0; width: 100%;
padding: 0; margin: 0;
color: var(--text_color); padding: 0;
color: var(--text_color);
} }
p { p {
@@ -52,21 +53,24 @@ p {
text-align: center; text-align: center;
} }
h1, h2, h3 { h1,
padding: 0; h2,
margin: 0; h3 {
font-weight: bold; padding: 0;
text-decoration: none; margin: 0;
white-space: pre; font-weight: bold;
text-align: left; text-decoration: none;
white-space: pre;
text-align: left;
} }
h1 { h1 {
color: var(--heading_text_color); color: var(--heading_text_color);
} }
h2, h3 { h2,
color: var(--heading_other_text_color); h3 {
color: var(--heading_other_text_color);
} }
p { p {
@@ -81,19 +85,23 @@ p {
overflow-y: scroll; overflow-y: scroll;
} }
.scrollable-content, ::-webkit-scrollbar { .scrollable-content,
width: 8px; ::-webkit-scrollbar {
height: 8px; width: 8px;
height: 8px;
} }
.scrollable-content, ::-webkit-scrollbar * { .scrollable-content,
background: transparent; ::-webkit-scrollbar * {
background: transparent;
} }
.scrollable-content, ::-webkit-scrollbar-thumb { .scrollable-content,
background: var(--control_background_hover) !important; ::-webkit-scrollbar-thumb {
background: var(--control_background_hover) !important;
} }
.scrollbar-corner, ::-webkit-scrollbar-corner { .scrollbar-corner,
background: transparent; ::-webkit-scrollbar-corner {
background: transparent;
} }

View File

@@ -3,16 +3,16 @@ import 'react-checkbox-tree/lib/react-checkbox-tree.css';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'; import { Provider } from 'react-redux';
import packageJson from '../package.json'; import packageJson from '../package.json';
import App from './App.jsx'; import App from './App.jsx';
import {setProviderState} from './redux/actions/mount_actions'; 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 { createAction } from '@reduxjs/toolkit';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {createAction} from '@reduxjs/toolkit'; import { getIPCRenderer } from '../../utils.jsx';
import {getIPCRenderer} from '../../utils';
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,58 +1,76 @@
import { createAction } from '@reduxjs/toolkit';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {createAction} from '@reduxjs/toolkit'; import { getIPCRenderer } from '../../utils.jsx';
import {getIPCRenderer} from '../../utils';
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(
break; installDependency(result.Destination, result.URL, isWinFSP)
case Constants.INSTALL_TYPES.Release: );
dispatch(installRelease(result.Destination)); break;
break; case Constants.INSTALL_TYPES.Release:
case Constants.INSTALL_TYPES.TestRelease: dispatch(installRelease(result.Destination));
dispatch(installAndTestRelease(result.Destination, testVersion, appPlatform)); break;
break; case Constants.INSTALL_TYPES.TestRelease:
case Constants.INSTALL_TYPES.Upgrade: dispatch(
//const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]]; installAndTestRelease(
const sha256 = null;//info.sha256; result.Destination,
const signature = null;//info.sig; testVersion,
dispatch(installUpgrade(result.Destination, sha256, signature, !!result.SkipVerification)); appPlatform
break; )
default: );
dispatch(notifyError('Unknown download type: ' + type)); break;
break; case Constants.INSTALL_TYPES.Upgrade:
// const info =
// this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]];
const sha256 = null; // info.sha256;
const signature = null; // info.sig;
dispatch(
installUpgrade(
result.Destination,
sha256,
signature,
!!result.SkipVerification
)
);
break;
default:
dispatch(notifyError('Unknown download type: ' + type));
break;
} }
} else { } else {
if (type === Constants.INSTALL_TYPES.TestRelease) { if (type === Constants.INSTALL_TYPES.TestRelease) {
@@ -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 { createAction } from '@reduxjs/toolkit';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {createAction} from '@reduxjs/toolkit'; import { getIPCRenderer, getSelectedVersionFromState } from '../../utils.jsx';
import {
getIPCRenderer,
getSelectedVersionFromState
} from '../../utils';
import {notifyError} from './error_actions';
import {downloadItem, setAllowDownload} from './download_actions';
import {
loadReleases,
setActiveRelease,
setInstalledVersion,
setReleaseUpgradeAvailable,
setNewReleasesAvailable2,
} from './release_version_actions';
import { 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 {unmountAll} from './mount_actions'; import { downloadItem, setAllowDownload } from './download_actions';
import { notifyError } from './error_actions';
import { unmountAll } from './mount_actions';
import {
loadReleases,
setActiveRelease,
setInstalledVersion,
setNewReleasesAvailable2,
setReleaseUpgradeAvailable,
} from './release_version_actions';
const ipcRenderer = getIPCRenderer(); 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,9 +120,9 @@ 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) {
dispatch(setRebootRequired()); dispatch(setRebootRequired());
} else { } else {
@@ -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(
File: dep.file, Constants.IPC_Check_Dependency_Installed + '_sync',
}); {
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(
// TODO Prompt to verify notifyError(
if (result.AllowSkipVerification) { result.Error,
dispatch(installUpgrade(source, sha256, signature, true)); false,
} () => {
}, false)); // TODO Prompt to verify
if (result.AllowSkipVerification) {
dispatch(installUpgrade(source, sha256, signature, true));
}
},
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,15 +139,13 @@ 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, return {type: SET_ALLOW_MOUNT, payload: {provider, allow}};
allow) => { };
return {type: SET_ALLOW_MOUNT, payload: {provider, allow}};
};
export const SET_AUTO_MOUNT_PROCESSED = 'mounts/setAutoMountProcessed'; export const SET_AUTO_MOUNT_PROCESSED = 'mounts/setAutoMountProcessed';
export const setAutoMountProcessed = (provider, processed) => { export const setAutoMountProcessed = (provider, processed) => {
@@ -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, return {type: SET_MOUNT_STATE, payload: {provider, state}};
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, return {type: SET_MOUNTED, payload: {provider, mounted}};
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 { createAction } from '@reduxjs/toolkit';
import axios from 'axios'; import axios from 'axios';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {createAction} from '@reduxjs/toolkit'; import {
import {notifyError} from './error_actions'; 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,55 +45,74 @@ 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());
}); });
}; };
}; };
export const loadReleases = () => { export const loadReleases = () => {
return (dispatch, getState) => { return (dispatch, getState) => {
const dispatchActions = (locationsLookup, versionLookup)=> { const dispatchActions = (locationsLookup, versionLookup) => {
const state = getState().relver; const state = getState().relver;
let release = state.Release; let release = state.Release;
if (release >= Constants.RELEASE_TYPES.length) { if (release >= Constants.RELEASE_TYPES.length) {
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(
dispatch(setActiveRelease(release, version)); unmountAll(() => {
dispatchActions(false); dispatch(setActiveRelease(release, version));
dispatch(showWindow()); dispatchActions(false);
dispatch(saveState()); dispatch(showWindow());
})); 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(
LocationsLookup: locationsLookup, 'releases',
VersionLookup: versionLookup JSON.stringify({
})); LocationsLookup: locationsLookup,
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,25 +225,35 @@ 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) => {
return { return {
type: SET_RELEASE_DATA, type: SET_RELEASE_DATA,
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,4 +1,4 @@
import {createAction} from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
export const displaySkynetExport = createAction('skynet/displaySkynetExport'); export const displaySkynetExport = createAction('skynet/displaySkynetExport');
export const displaySkynetImport = createAction('skynet/displaySkynetImport'); export const displaySkynetImport = createAction('skynet/displaySkynetImport');

View File

@@ -1,71 +1,66 @@
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, {
AppBusy: false, AllowMount: false,
AppBusyTransparent: false, AppBusy: false,
AppPlatform: platformInfo.AppPlatform, AppBusyTransparent: false,
AppReady: false, AppPlatform: platformInfo.AppPlatform,
DisplayConfirmYesNo: false, AppReady: false,
ConfirmTitle: null, DisplayConfirmYesNo: false,
DisplaySelectAppPlatform: false, ConfirmTitle: null,
Platform: platformInfo.Platform, DisplaySelectAppPlatform: false,
RebootRequired: false, Platform: platformInfo.Platform,
Version: version, RebootRequired: false,
}, { Version: version,
[DISPLAY_CONFIRM_YES_NO]: (state, action) => {
return {
...state,
DisplayConfirmYesNo: action.payload.show,
ConfirmTitle: action.payload.show ? action.payload.title : null,
}
}, },
[SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => { {
return { [DISPLAY_CONFIRM_YES_NO]: (state, action) => {
...state, return {
DisplaySelectAppPlatform: action.payload, ...state,
} DisplayConfirmYesNo: action.payload.show,
}, ConfirmTitle: action.payload.show ? action.payload.title : null,
[setAllowMount]: (state, action) => { };
return { },
...state, [SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => {
AllowMount: action.payload, return { ...state, DisplaySelectAppPlatform: action.payload };
} },
}, [setAllowMount]: (state, action) => {
[setApplicationReady]: (state, action) => { return { ...state, AllowMount: action.payload };
return { },
...state, [setApplicationReady]: (state, action) => {
AppReady: action.payload, return {
}; ...state,
}, AppReady: action.payload,
[setLinuxAppPlatform]: (state, action) => { };
return { },
...state, [setLinuxAppPlatform]: (state, action) => {
AppPlatform: action.payload, return { ...state, AppPlatform: action.payload };
} },
}, [NOTIFY_APPLICATION_BUSY]: (state, action) => {
[NOTIFY_APPLICATION_BUSY]: (state, action) => { return {
return { ...state,
...state, AppBusy: action.payload.busy,
AppBusy: action.payload.busy, AppBusyTransparent: action.payload.busy && action.payload.transparent,
AppBusyTransparent: action.payload.busy && action.payload.transparent, };
}; },
}, [notifyRebootRequired]: (state, action) => {
[notifyRebootRequired]: (state, action) => { return {
return { ...state,
...state, 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,37 +18,37 @@ const defaultDownloadState = {
DownloadType: null, DownloadType: null,
}; };
export const downloadReducer = createReducer({ export const downloadReducer = createReducer(
...defaultDownloadState, {
AllowDownload: false, ...defaultDownloadState,
}, { AllowDownload: false,
[setAllowDownload]: (state, action) => {
return {
...state,
AllowDownload: action.payload,
};
}, },
[SET_DOWNLOAD_BEGIN]: (state, action) => { {
return { [setAllowDownload]: (state, action) => {
...state, return {
...defaultDownloadState, ...state,
DownloadActive: true, AllowDownload: action.payload,
DownloadName: action.payload.name, };
DownloadType: action.payload.type, },
DownloadURL: action.payload.url, [SET_DOWNLOAD_BEGIN]: (state, action) => {
} return {
}, ...state,
[setDownloadEnd]: (state, action) => { ...defaultDownloadState,
return { DownloadActive: true,
...state, DownloadName: action.payload.name,
...defaultDownloadState, DownloadType: action.payload.type,
DownloadResult: action.payload, DownloadURL: action.payload.url,
}; };
}, },
[setDownloadProgress]: (state, action) => { [setDownloadEnd]: (state, action) => {
return { return {
...state, ...state,
DownloadProgress: action.payload, ...defaultDownloadState,
} DownloadResult: action.payload,
};
},
[setDownloadProgress]: (state, action) => {
return { ...state, DownloadProgress: action.payload };
},
} }
}); );

View File

@@ -1,52 +1,53 @@
import {createReducer} from '@reduxjs/toolkit'; import { createReducer } from '@reduxjs/toolkit';
import { 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, {
DisplayInfo: false, DisplayError: false,
ErrorCritical: false, DisplayInfo: false,
ErrorStack: [], ErrorCritical: false,
InfoStack: [], ErrorStack: [],
}, { InfoStack: [],
[CLEAR_ERROR]: state => {
const errorStack = (state.ErrorStack.length > 0) ? state.ErrorStack.slice(1) : [];
return {
...state,
DisplayError: (errorStack.length > 0),
ErrorStack: errorStack,
}
}, },
[CLEAR_INFO]: state => { {
const infoStack = (state.InfoStack.length > 0) ? state.InfoStack.slice(1) : []; [CLEAR_ERROR]: (state) => {
return { const errorStack =
...state, state.ErrorStack.length > 0 ? state.ErrorStack.slice(1) : [];
DisplayInfo: (infoStack.length > 0), return {
InfoStack: infoStack, ...state,
} DisplayError: errorStack.length > 0,
}, ErrorStack: errorStack,
[SET_ERROR_INFO]: (state, action) => { };
const errorStack = [action.payload.msg, ...state.ErrorStack]; },
return { [CLEAR_INFO]: (state) => {
...state, const infoStack =
DisplayError: true, state.InfoStack.length > 0 ? state.InfoStack.slice(1) : [];
ErrorCritical: state.ErrorCritical || action.payload.critical, return {
ErrorStack: errorStack, ...state,
} DisplayInfo: infoStack.length > 0,
}, InfoStack: infoStack,
[SET_INFO]: (state, action) => { };
const infoStack = [{ },
title: action.payload.title, [SET_ERROR_INFO]: (state, action) => {
message: action.payload.msg const errorStack = [action.payload.msg, ...state.ErrorStack];
}, ...state.InfoStack]; return {
return { ...state,
...state, DisplayError: true,
DisplayInfo: true, ErrorCritical: state.ErrorCritical || action.payload.critical,
InfoStack: infoStack, ErrorStack: errorStack,
} };
},
[SET_INFO]: (state, action) => {
const infoStack = [
{ title: action.payload.title, message: action.payload.msg },
...state.InfoStack,
];
return { ...state, DisplayInfo: true, InfoStack: infoStack };
},
} }
}); );

View File

@@ -1,60 +1,51 @@
import {createReducer} from '@reduxjs/toolkit'; import { createReducer } from '@reduxjs/toolkit';
import { import {
setAutoInstallRelease, setAutoInstallRelease,
setDismissDependencies, setDismissDependencies,
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, {
DismissDependencies: false, AutoInstallRelease: false,
InstallActive: false, DismissDependencies: false,
InstallResult: null, InstallActive: false,
InstallTestActive: false, InstallResult: null,
InstallType: null, InstallTestActive: false,
MissingDependencies: [], InstallType: null,
}, { MissingDependencies: [],
[setAutoInstallRelease]: (state, action) => {
return {
...state,
AutoInstallRelease: action.payload,
}
}, },
[setDismissDependencies]: (state, action) => { {
return { [setAutoInstallRelease]: (state, action) => {
...state, return { ...state, AutoInstallRelease: action.payload };
DismissDependencies: action.payload, },
} [setDismissDependencies]: (state, action) => {
}, return { ...state, DismissDependencies: action.payload };
[setInstallActive]: (state, action) => { },
return { [setInstallActive]: (state, action) => {
...state, return {
InstallActive: true, ...state,
InstallResult: null, InstallActive: true,
InstallType: action.payload, InstallResult: null,
}; InstallType: action.payload,
}, };
[setInstallComplete]: (state, action) => { },
return { [setInstallComplete]: (state, action) => {
...state, return {
InstallActive: false, ...state,
InstallResult: action.payload, InstallActive: false,
InstallType: null, InstallResult: action.payload,
} InstallType: null,
}, };
[setInstallTestActive]: (state, action) => { },
return { [setInstallTestActive]: (state, action) => {
...state, return { ...state, InstallTestActive: action.payload };
InstallTestActive: action.payload, },
} [setMissingDependencies]: (state, action) => {
}, return { ...state, MissingDependencies: action.payload };
[setMissingDependencies]: (state, action) => { },
return {
...state,
MissingDependencies: action.payload,
}
} }
}); );

View File

@@ -1,4 +1,4 @@
import {createReducer} from '@reduxjs/toolkit'; import { createReducer } from '@reduxjs/toolkit';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import { import {
@@ -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) => {
return {...map, ...obj}
});
const mountState = providerList
.map(provider => {
return {
[provider]: {
AllowMount: false,
DriveLetters: [],
Mounted: false,
}
}
})
.reduce((map, obj) => {
return {...map, ...obj}
});
const autoMountProcessed =
providerList.map(provider => {
return {[provider]: false,}
}) })
.reduce((map, obj) => { .reduce((map, obj) => {
return {...map, ...obj} return { ...map, ...obj };
});
const mountState = providerList
.map((provider) => {
return {
[provider]: {
AllowMount: false,
DriveLetters: [],
Mounted: false,
},
};
})
.reduce((map, obj) => {
return { ...map, ...obj };
});
const autoMountProcessed = providerList
.map((provider) => {
return { [provider]: false };
})
.reduce((map, obj) => {
return { ...map, ...obj };
}); });
return createReducer( return createReducer(
@@ -71,52 +71,56 @@ export const createMountReducer = state => {
}, },
{ {
[addRemoteMount2]: (state, action) => { [addRemoteMount2]: (state, action) => {
let mountState = {...state.MountState}; let mountState = { ...state.MountState };
mountState[action.payload] = { mountState[action.payload] = {
AllowMount: false, AllowMount: false,
DriveLetters: [], DriveLetters: [],
Mounted: false, Mounted: false,
}; };
let providerState = {...state.ProviderState}; let providerState = { ...state.ProviderState };
providerState[action.payload] = { providerState[action.payload] = {
AutoMount: false, AutoMount: false,
AutoRestart: false, AutoRestart: false,
MountLocation: '', MountLocation: '',
}; };
let autoMountProcessed = {...state.AutoMountProcessed}; let autoMountProcessed = { ...state.AutoMountProcessed };
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 };
mountState[action.payload] = { mountState[action.payload] = {
AllowMount: false, AllowMount: false,
DriveLetters: [], DriveLetters: [],
Mounted: false, Mounted: false,
}; };
let providerState = {...state.ProviderState}; let providerState = { ...state.ProviderState };
providerState[action.payload] = { providerState[action.payload] = {
AutoMount: false, AutoMount: false,
AutoRestart: false, AutoRestart: false,
MountLocation: '', MountLocation: '',
}; };
let autoMountProcessed = {...state.AutoMountProcessed}; let autoMountProcessed = { ...state.AutoMountProcessed };
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 {
@@ -127,19 +131,19 @@ export const createMountReducer = state => {
}; };
}, },
[removeMount3]: (state, action) => { [removeMount3]: (state, action) => {
let mountState = {...state.MountState}; let mountState = { ...state.MountState };
delete mountState[action.payload]; delete mountState[action.payload];
let providerState = {...state.ProviderState}; let providerState = { ...state.ProviderState };
delete providerState[action.payload]; delete providerState[action.payload];
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,15 +173,13 @@ 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, return { ...state, MountsBusy: action.payload };
action) => { },
return {...state, MountsBusy: action.payload};
},
[SET_MOUNT_STATE]: (state, action) => { [SET_MOUNT_STATE]: (state, action) => {
return { return {
...state, ...state,
@@ -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) => {
return {
...state,
DisplayPinnedManager: action.payload,
};
}, },
}); {
[displayPinnedManager]: (state, action) => {
return {
...state,
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,95 +12,95 @@ const versionLookup = Constants.RELEASE_TYPES.map(k=> {
}; };
}); });
export const releaseVersionReducer = createReducer({ export const releaseVersionReducer = createReducer(
AllowDismissDependencies: false, {
DismissNewReleasesAvailable: true, AllowDismissDependencies: false,
InstalledVersion: 'none', DismissNewReleasesAvailable: true,
LocationsLookup: {}, InstalledVersion: 'none',
NewReleasesAvailable: [], LocationsLookup: {},
NewReleasesAvailable2: [], NewReleasesAvailable: [],
Release: Constants.DEFAULT_RELEASE, NewReleasesAvailable2: [],
ReleaseUpgradeAvailable: false, Release: Constants.DEFAULT_RELEASE,
UpgradeAvailable: false, ReleaseUpgradeAvailable: false,
UpgradeData: null, UpgradeAvailable: false,
UpgradeVersion: null, UpgradeData: null,
UpgradeDismissed: false, UpgradeVersion: null,
Version: -1, UpgradeDismissed: false,
VersionLookup: versionLookup, Version: -1,
}, { VersionLookup: versionLookup,
[Actions.CLEAR_UI_UPGRADE]: state => {
return {
...state,
UpgradeAvailable: false,
UpgradeDismissed: false,
UpgradeData: null,
UpgradeVersion: null,
};
}, },
[Actions.NOTIFY_ACTIVE_RELEASE]: (state, action) => { {
return { [Actions.CLEAR_UI_UPGRADE]: (state) => {
...state, return {
Release: action.payload.release, ...state,
Version: action.payload.version UpgradeAvailable: false,
}; UpgradeDismissed: false,
}, UpgradeData: null,
[Actions.setAllowDismissDependencies]: (state, action) => { UpgradeVersion: null,
return { };
...state, },
AllowDismissDependencies: action.payload, [Actions.NOTIFY_ACTIVE_RELEASE]: (state, action) => {
}; return {
}, ...state,
[Actions.setDismissNewReleasesAvailable]: (state, action) => { Release: action.payload.release,
return { Version: action.payload.version,
...state, };
DismissNewReleasesAvailable: action.payload, },
}; [Actions.setAllowDismissDependencies]: (state, action) => {
}, return {
[Actions.setDismissUIUpgrade]: (state, action) => { ...state,
return { AllowDismissDependencies: action.payload,
...state, };
UpgradeDismissed: action.payload, },
}; [Actions.setDismissNewReleasesAvailable]: (state, action) => {
}, return {
[Actions.setInstalledVersion]: (state, action) => { ...state,
return { DismissNewReleasesAvailable: action.payload,
...state, };
InstalledVersion: action.payload, },
} [Actions.setDismissUIUpgrade]: (state, action) => {
}, return {
[Actions.setNewReleasesAvailable]: (state, action) => { ...state,
return { UpgradeDismissed: action.payload,
...state, };
DismissNewReleasesAvailable: false, },
NewReleasesAvailable: action.payload, [Actions.setInstalledVersion]: (state, action) => {
}; return { ...state, InstalledVersion: action.payload };
}, },
[Actions.setNewReleasesAvailable2]: (state, action) => { [Actions.setNewReleasesAvailable]: (state, action) => {
return { return {
...state, ...state,
NewReleasesAvailable2: action.payload, DismissNewReleasesAvailable: false,
}; NewReleasesAvailable: action.payload,
}, };
[Actions.SET_RELEASE_DATA]: (state, action) => { },
return { [Actions.setNewReleasesAvailable2]: (state, action) => {
...state, return {
LocationsLookup: action.payload.locations, ...state,
VersionLookup: action.payload.versions, NewReleasesAvailable2: action.payload,
}; };
}, },
[Actions.setReleaseUpgradeAvailable]: (state, action) => { [Actions.SET_RELEASE_DATA]: (state, action) => {
return { return {
...state, ...state,
ReleaseUpgradeAvailable: action.payload, LocationsLookup: action.payload.locations,
}; VersionLookup: action.payload.versions,
}, };
[Actions.SET_UI_UPGRADE_DATA]: (state, action) => { },
return { [Actions.setReleaseUpgradeAvailable]: (state, action) => {
...state, return {
UpgradeAvailable: true, ...state,
UpgradeData: action.payload.upgrade_data, ReleaseUpgradeAvailable: action.payload,
UpgradeVersion: action.payload.version, };
UpgradeDismissed: false, },
}; [Actions.SET_UI_UPGRADE_DATA]: (state, action) => {
return {
...state,
UpgradeAvailable: true,
UpgradeData: action.payload.upgrade_data,
UpgradeVersion: action.payload.version,
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, {
DisplayImport: false, DisplayExport: false,
}, { DisplayImport: false,
[Actions.displaySkynetExport]: (state, action) => {
return {
...state,
DisplayExport: action.payload,
}
}, },
[Actions.displaySkynetImport]: (state, action) => { {
return { [Actions.displaySkynetExport]: (state, action) => {
...state, return { ...state, DisplayExport: action.payload };
DisplayImport: action.payload, },
} [Actions.displaySkynetImport]: (state, action) => {
}, return { ...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 {downloadReducer} from '../reducers/download_reducer'; import { createCommonReducer } from '../reducers/common_reducer';
import {errorReducer} from '../reducers/error_reducer'; import { downloadReducer } from '../reducers/download_reducer';
import {installReducer} from '../reducers/install_reducer'; import { errorReducer } from '../reducers/error_reducer';
import {createMountReducer} from '../reducers/mount_reducer'; import { installReducer } from '../reducers/install_reducer';
import {releaseVersionReducer} from '../reducers/release_version_reducer'; import { createMountReducer } from '../reducers/mount_reducer';
import {skynetReducer} from '../reducers/skynet_reducer'; import { pinnedManagerReducer } from '../reducers/pinned_manager_reducer';
import {pinnedManagerReducer} from '../reducers/pinned_manager_reducer' import { releaseVersionReducer } from '../reducers/release_version_reducer';
import { skynetReducer } from '../reducers/skynet_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

@@ -1,6 +1,6 @@
const Constants = require('../../constants'); const Constants = require('../../constants');
const addListeners = (ipcMain, {closeApplication, setWindowVisibility}) => { const addListeners = (ipcMain, { closeApplication, setWindowVisibility }) => {
ipcMain.on(Constants.IPC_Shutdown, () => { ipcMain.on(Constants.IPC_Shutdown, () => {
closeApplication(); closeApplication();
}); });
@@ -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

@@ -1,48 +1,70 @@
const Constants = require('../../constants'); const Constants = require('../../constants');
const helpers = require('../../helpers'); const helpers = require('../../helpers');
const addListeners = (ipcMain, {standardIPCReply}) => { const addListeners = (ipcMain, { standardIPCReply }) => {
ipcMain.on(Constants.IPC_Get_Config, (event, data) => { ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
helpers helpers
.getConfig(data.Version, data.Provider, data.Remote, data.S3) .getConfig(data.Version, data.Provider, data.Remote, data.S3)
.then((data) => { .then((data) => {
if (data.Code === 0) { if (data.Code === 0) {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, { standardIPCReply(event, Constants.IPC_Get_Config_Reply, {
Config: data.Data, Config: data.Data,
}); });
} else { } else {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, data.Code); standardIPCReply(
} event,
}) Constants.IPC_Get_Config_Reply,
.catch(error => { {},
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error); data.Code
}); );
}
})
.catch((error) => {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error);
});
}); });
ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => { ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => {
helpers helpers
.getConfigTemplate(data.Version, data.Provider, data.Remote, data.S3) .getConfigTemplate(data.Version, data.Provider, data.Remote, data.S3)
.then((data) => { .then((data) => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, { standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {
Template: data, Template: data,
});
})
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Get_Config_Template_Reply,
{},
error
);
}); });
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error);
});
}); });
ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => { ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => {
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(
.then(() => { data.Items[i].Name,
setConfigValue(++i); data.Items[i].Value,
}) data.Provider,
.catch(error => { data.Remote,
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}, error); data.S3,
}); data.Version
)
.then(() => {
setConfigValue(++i);
})
.catch((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

@@ -1,47 +1,46 @@
const Constants = require('../../constants'); const Constants = require('../../constants');
const helpers = require('../../helpers'); const helpers = require('../../helpers');
const addListeners = (ipcMain, {standardIPCReply}) => { 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) => {
standardIPCReply(
event,
Constants.IPC_Check_Daemon_Version_Reply,
{
Valid: false,
},
e
);
}); });
})
.catch(e => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, {
Valid: false,
}, 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

@@ -2,7 +2,7 @@ const Constants = require('../../constants');
const fs = require('fs'); const fs = require('fs');
const helpers = require('../../helpers'); const helpers = require('../../helpers');
const addListeners = (ipcMain, {standardIPCReply}) => { const addListeners = (ipcMain, { standardIPCReply }) => {
ipcMain.on(Constants.IPC_Check_Dependency_Installed, (event, data) => { ipcMain.on(Constants.IPC_Check_Dependency_Installed, (event, data) => {
try { try {
const exists = fs.lstatSync(data.File).isFile(); const exists = fs.lstatSync(data.File).isFile();
@@ -13,83 +13,99 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}); });
} catch (e) { } catch (e) {
standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, { standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, {
data : { data: {
Exists: false, Exists: false,
}, },
}); });
} }
}); });
ipcMain.on(Constants.IPC_Check_Dependency_Installed + '_sync', (event, data) => { ipcMain.on(
try { Constants.IPC_Check_Dependency_Installed + '_sync',
const ls = fs.lstatSync(data.File); (event, data) => {
event.returnValue = { try {
data: { const ls = fs.lstatSync(data.File);
Exists: ls.isFile() || ls.isSymbolicLink(), event.returnValue = {
}, data: {
}; Exists: ls.isFile() || ls.isSymbolicLink(),
} catch (e) { },
event.returnValue = { };
data: { } catch (e) {
Exists: false event.returnValue = {
}, data: { 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')) {
helpers helpers
.executeAsync('open', ['-a', 'Finder', '-W', data.Source]) .executeAsync('open', ['-a', 'Finder', '-W', data.Source])
.then(() => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
});
})
.catch(error=> {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
}, error);
});
} else {
const execInstall = () => {
helpers
.executeAndWait(data.Source)
.then(() => { .then(() => {
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,
}); });
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(
Source: data.Source, event,
URL: data.URL, Constants.IPC_Install_Dependency_Reply,
}, error); {
Source: data.Source,
URL: data.URL,
},
error
);
}); });
}; } else {
if (data.IsWinFSP) { const execInstall = () => {
helpers helpers
.performWindowsUninstall(Constants.WINFSP_VERSION_NAMES) .executeAndWait(data.Source)
.then(uninstalled => { .then(() => {
if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
RebootRequired: true,
Source: data.Source, Source: data.Source,
URL: data.URL, URL: data.URL,
}); });
} else { })
execInstall(); .catch((error) => {
} standardIPCReply(
}) event,
.catch(error => { 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) {
helpers
.performWindowsUninstall(Constants.WINFSP_VERSION_NAMES)
.then((uninstalled) => {
if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
RebootRequired: true,
Source: data.Source,
URL: data.URL,
});
} else {
execInstall();
}
})
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Install_Dependency_Reply,
{
Source: data.Source,
URL: data.URL,
},
error
);
});
} else { } else {
execInstall(); execInstall();
} }
@@ -97,6 +113,4 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -2,24 +2,32 @@ const Constants = require('../../constants');
const helpers = require('../../helpers'); const helpers = require('../../helpers');
const path = require('path'); 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(
standardIPCReply(event, Constants.IPC_Download_File_Progress, { data.URL,
Destination: destination, destination,
Progress: progress, (progress) => {
URL: data.URL, standardIPCReply(event, Constants.IPC_Download_File_Progress, {
}); Destination: destination,
}, error => { Progress: progress,
standardIPCReply(event, Constants.IPC_Download_File_Complete, { URL: data.URL,
Destination: destination, });
URL: data.URL, },
}, error); (error) => {
}); standardIPCReply(
event,
Constants.IPC_Download_File_Complete,
{
Destination: destination,
URL: data.URL,
},
error
);
}
);
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -1,19 +1,23 @@
const Constants = require('../../constants'); const Constants = require('../../constants');
const fs = require('fs'); 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(
defaultPath: data.Location, getMainWindow(),
properties: ['openDirectory'], {
title: data.Title, defaultPath: data.Location,
}, (filePaths) => { properties: ['openDirectory'],
if (filePaths && (filePaths.length > 0)) { title: data.Title,
event.returnValue = filePaths[0]; },
} else { (filePaths) => {
event.returnValue = ''; if (filePaths && filePaths.length > 0) {
event.returnValue = filePaths[0];
} else {
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,38 +10,45 @@ 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);
sender.send(Constants.IPC_Unmount_Drive_Reply, { sender.send(Constants.IPC_Unmount_Drive_Reply, {
data: { data: {
Expected: expectedUnmount[provider], Expected: expectedUnmount[provider],
Location: location, Location: 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,22 +62,27 @@ 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 = [];
mountedData = {}; mountedData = {};
}; };
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 (
return driveLetter === locations[provider]; !driveLetters[provider].find((driveLetter) => {
})) { return driveLetter === locations[provider];
})
) {
driveLetters[provider].push(locations[provider]); driveLetters[provider].push(locations[provider]);
} }
} }
@@ -135,66 +147,79 @@ 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
.detectRepertoryMounts(data.Version, providerList) .detectRepertoryMounts(data.Version, providerList)
.then((results) => { .then((results) => {
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]
Active: false, ? results[provider]
Location: '', : {
PID: -1, Active: false,
}; Location: '',
locations[provider] = storageData[provider].Location; PID: -1,
};
locations[provider] = storageData[provider].Location;
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
);
}
} }
} }
}
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
grabDriveLetters(locations); grabDriveLetters(locations);
} }
setImage(locations); setImage(locations);
if (firstMountCheck) { if (firstMountCheck) {
firstMountCheck = false; firstMountCheck = false;
} }
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, { standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
Active: storageData[provider].Active, Active: storageData[provider].Active,
DriveLetters: driveLetters[provider], DriveLetters: driveLetters[provider],
Location: locations[provider], Location: locations[provider],
PID: storageData[provider].PID, PID: storageData[provider].PID,
Provider: provider, Provider: provider,
});
})
.catch((error) => {
if (os.platform() === 'win32') {
grabDriveLetters({});
}
setImage({});
standardIPCReply(
event,
Constants.IPC_Detect_Mount_Reply,
{
DriveLetters: driveLetters[provider],
Provider: provider,
},
error
);
}); });
})
.catch(error => {
if (os.platform() === 'win32') {
grabDriveLetters({});
}
setImage({});
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
DriveLetters: driveLetters[provider],
Provider: provider,
}, error);
});
}); });
ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => { ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
@@ -216,28 +241,40 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
delete mountedData[data.Location]; delete mountedData[data.Location];
} }
standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, { standardIPCReply(
Expected: expectedUnmount[data.Provider], event,
Location: data.Location, Constants.IPC_Unmount_Drive_Reply,
Provider: data.Provider, {
Remote: data.Remote, Expected: expectedUnmount[data.Provider],
S3: data.S3, Location: data.Location,
}, error || Error(data.Provider + ' Unmounted')); Provider: data.Provider,
Remote: data.Remote,
S3: data.S3,
},
error || Error(data.Provider + ' Unmounted')
);
}; };
helpers helpers
.executeMount(data.Version, data.Provider, data.Remote, data.S3, data.Location, (error, pid) => { .executeMount(
errorHandler(pid, error); data.Version,
}) data.Provider,
.then(() => { data.Remote,
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, { data.S3,
Provider: data.Provider, data.Location,
Remote: data.Remote, (error, pid) => {
S3: data.S3, errorHandler(pid, error);
}
)
.then(() => {
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
Provider: data.Provider,
Remote: data.Remote,
S3: data.S3,
});
})
.catch((error) => {
errorHandler(-1, error);
}); });
})
.catch(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);
}); });
@@ -265,17 +316,17 @@ 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);
}); });
}); });
}; };
module.exports = { module.exports = {
addListeners, addListeners,
unmountAllDrives unmountAllDrives,
}; };

View File

@@ -1,49 +1,64 @@
const Constants = require('../../constants'); const Constants = require('../../constants');
const helpers = require('../../helpers'); 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,
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, { data.Version,
Items: data.items, data.Provider,
data.Remote,
data.S3
)
.then((data) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
Items: data.items,
});
})
.catch((e) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
}); });
})
.catch(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,
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, { data.Version,
Items: data.items, data.Provider,
data.Remote,
data.S3
)
.then((data) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
Items: data.items,
});
})
.catch((e) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
}); });
})
.catch(e => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
});
}); });
ipcMain.on(Constants.IPC_Get_Pinned_Files_Status, (event, data) => { ipcMain.on(Constants.IPC_Get_Pinned_Files_Status, (event, data) => {});
});
ipcMain.on(Constants.IPC_Set_Pinned + '_sync', (event, data) => { 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,
event.returnValue = success; data.Pinned,
}) data.Version,
.catch(e => { data.Provider,
event.returnValue = false; data.Remote,
}); data.S3
)
.then((success) => {
event.returnValue = success;
})
.catch((e) => {
event.returnValue = false;
});
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -10,11 +10,11 @@ const getPlatformOverride = () => {
return _platformOverride; return _platformOverride;
}; };
const setPlatformOverride = platformOverride => { const setPlatformOverride = (platformOverride) => {
_platformOverride = platformOverride; _platformOverride = platformOverride;
}; };
const addListeners = (ipcMain, {detectScript, saveUiSettings}) => { const addListeners = (ipcMain, { detectScript, saveUiSettings }) => {
ipcMain.on(Constants.IPC_Get_Platform, (event) => { ipcMain.on(Constants.IPC_Get_Platform, (event) => {
const sendResponse = (appPlatform, platform) => { const sendResponse = (appPlatform, platform) => {
event.sender.send(Constants.IPC_Get_Platform_Reply, { event.sender.send(Constants.IPC_Get_Platform_Reply, {
@@ -25,40 +25,44 @@ 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');
fs.writeFileSync(scriptFile, detectScript); fs.writeFileSync(scriptFile, detectScript);
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,
if (err) { scriptFile,
sendResponse(appPlatform, platform); null,
} else { (err) => {
helpers if (err) {
.executeScript(scriptFile) sendResponse(appPlatform, platform);
.then(data => { } else {
appPlatform = data.replace(/(\r\n|\n|\r)/gm, ""); helpers
sendResponse(appPlatform, platform); .executeScript(scriptFile)
}) .then((data) => {
.catch(() => { appPlatform = data.replace(/(\r\n|\n|\r)/gm, '');
sendResponse(appPlatform, platform); sendResponse(appPlatform, platform);
}); })
} .catch(() => {
}); sendResponse(appPlatform, platform);
} else { });
sendResponse(appPlatform, platform); }
} }
}) );
.catch(() => { } else {
sendResponse(platform, platform); sendResponse(appPlatform, platform);
}); }
})
.catch(() => {
sendResponse(platform, platform);
});
} }
} else { } else {
sendResponse(platform, platform); sendResponse(platform, platform);
@@ -75,5 +79,5 @@ const addListeners = (ipcMain, {detectScript, saveUiSettings}) => {
module.exports = { module.exports = {
getPlatformOverride, getPlatformOverride,
setPlatformOverride, setPlatformOverride,
addListeners addListeners,
}; };

View File

@@ -5,28 +5,35 @@ const os = require('os');
const path = require('path'); const path = require('path');
const unzip = require('unzipper'); const unzip = require('unzipper');
const addListeners = (ipcMain, {getCleanupReleases, standardIPCReply}) => { const addListeners = (ipcMain, { getCleanupReleases, standardIPCReply }) => {
ipcMain.on(Constants.IPC_Check_Installed, (event, data) => { ipcMain.on(Constants.IPC_Check_Installed, (event, data) => {
const destination = path.join(helpers.getDataDirectory(), data.Version); const destination = path.join(helpers.getDataDirectory(), data.Version);
helpers helpers
.getMissingDependencies(data.Dependencies) .getMissingDependencies(data.Dependencies)
.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();
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, { } catch (e) {}
Dependencies: dependencies, standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
Exists: exists, Dependencies: dependencies,
Version: data.Version, Exists: exists,
Version: data.Version,
});
})
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Check_Installed_Reply,
{
Dependencies: [],
Version: data.Version,
},
error
);
}); });
}).catch(error => {
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
Dependencies: [],
Version: data.Version,
}, error);
});
}); });
ipcMain.on(Constants.IPC_Cleanup_Releases + '_sync', (event, data) => { ipcMain.on(Constants.IPC_Cleanup_Releases + '_sync', (event, data) => {
@@ -44,52 +51,61 @@ 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(
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { event,
Source: data.Source, Constants.IPC_Extract_Release_Complete,
}, error); {
}) Source: data.Source,
.on('finish', () => { },
stream.close(); error
if (os.platform() !== 'win32') { );
helpers })
.executeAndWait("chmod +x \"" + path.join(destination, 'repertory') + "\"") .on('finish', () => {
.then(() => { stream.close();
if (os.platform() !== 'win32') {
helpers
.executeAndWait(
'chmod +x "' + path.join(destination, 'repertory') + '"'
)
.then(() => {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
});
})
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Extract_Release_Complete,
{
Source: data.Source,
},
error
);
});
} else {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, { standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source, Source: data.Source,
}); });
}) }
.catch(error => { });
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
}, error);
})
} else {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
});
}
});
}); });
ipcMain.on(Constants.IPC_Test_Release, (event, data) => { ipcMain.on(Constants.IPC_Test_Release, (event, data) => {
helpers helpers
.testRepertoryBinary(data.Version) .testRepertoryBinary(data.Version)
.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

@@ -1,47 +1,50 @@
const Constants = require('../../constants'); const Constants = require('../../constants');
const helpers = require('../../helpers'); const helpers = require('../../helpers');
const addListeners = (ipcMain, {standardIPCReply}) => { 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) => {
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {}, error);
}); });
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {}, error);
});
}); });
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) => {
standardIPCReply(
event,
Constants.IPC_Grab_Skynet_Tree_Reply,
{},
error
);
}); });
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {}, error);
});
}); });
ipcMain.on(Constants.IPC_Import_Skylinks, (event, data) => { 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) => {
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {}, error);
}); });
})
.catch(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

@@ -2,7 +2,7 @@ const Constants = require('../../constants');
const os = require('os'); const os = require('os');
const helpers = require('../../helpers'); const helpers = require('../../helpers');
const addListeners = (ipcMain, {closeApplication}) => { const addListeners = (ipcMain, { closeApplication }) => {
ipcMain.on(Constants.IPC_Reboot_System, () => { ipcMain.on(Constants.IPC_Reboot_System, () => {
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']); helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']);
@@ -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(
AllowSkipVerification: allowSkipVerification, event,
Source: data.Source, Constants.IPC_Install_Upgrade_Reply,
}, err); {
AllowSkipVerification: allowSkipVerification,
Source: data.Source,
},
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) {
@@ -69,35 +81,37 @@ const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCRe
const executeInstall = () => { const executeInstall = () => {
setIsInstalling(true); setIsInstalling(true);
helpers helpers
.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);
}); });
}; };
if (hasSignature) { if (hasSignature) {
helpers helpers
.verifySignature(data.Source, tempSig, tempPub) .verifySignature(data.Source, tempSig, tempPub)
.then(() => { .then(() => {
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
.verifyHash(data.Source, data.Sha256) .verifyHash(data.Source, data.Sha256)
.then(()=> { .then(() => {
executeInstall(); executeInstall();
}) })
.catch(() => { .catch(() => {
errorHandler(Error('Failed to verify installation package hash')); errorHandler(Error('Failed to verify installation package hash'));
}); });
} else { } else {
if (platform === 'darwin') { if (platform === 'darwin') {
setTimeout(executeInstall, 3000); setTimeout(executeInstall, 3000);
@@ -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
];
};