Merged 1.3.x_branch into master

This commit is contained in:
2021-05-20 23:28:19 +00:00
114 changed files with 6031 additions and 4106 deletions

30
.eslintrc.json Normal file
View File

@@ -0,0 +1,30 @@
{
"root":true,
"env": {
"browser": true,
"es2021": true,
"node": true,
"jest/globals": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:prettier/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"prettier",
"react",
"jest"
],
"rules": {
"prettier/prettier": "warn"
}
}

8
.prettierrc.json Normal file
View File

@@ -0,0 +1,8 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxBracketSameLine": true,
"printWidth": 100
}

View File

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

2
.vimrc
View File

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

View File

@@ -1,150 +1,178 @@
# Changelog # Changelog
## 1.3.4
- \#55: Enable 'Activate' release instead of 'Install' in notification pop-up
- \#58: Add authentication support for Skynet premium portals
- \#59: Add agent string / API key support for Skynet portals
- Ability to export Skylink's to json file
- Ability to import Skylink's from json file
## 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

@@ -12,13 +12,13 @@ Skynet support is considered EXPERIMENTAL. Files added to Skynet should not be c
* ScPrime >=1.4.1.2 * ScPrime >=1.4.1.2
## Downloads ## Downloads
* **Repertory UI v1.3.3 Linux * **Repertory UI v1.3.4 Linux
64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_linux_x86_64.AppImage) 64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_linux_x86_64.AppImage)
* NOTE: Linux distributions require `fuse` and `libfuse` to be installed. * NOTE: Linux distributions require `fuse` and `libfuse` to be installed.
* **Repertory UI v1.3.3 OS X * **Repertory UI v1.3.4 OS X
64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_mac.dmg) 64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_mac.dmg)
* **Repertory UI v1.3.3 Windows * **Repertory UI v1.3.4 Windows
64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_win.exe) 64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_win.exe)
## Supported Platforms ## Supported Platforms
* OS X 64-bit * OS X 64-bit

View File

@@ -26,7 +26,7 @@ if beginsWith darwin "$OSTYPE"; then
JQ_EXEC=jq-osx-amd64 JQ_EXEC=jq-osx-amd64
SHA256_EXEC="shasum -a 256 -b" SHA256_EXEC="shasum -a 256 -b"
else else
DISTRO_LIST="centos7 debian9 debian10 solus" DISTRO_LIST="centos7 solus"
OUT_FILE=repertory-ui_${APP_VER}_linux_x86_64.AppImage OUT_FILE=repertory-ui_${APP_VER}_linux_x86_64.AppImage
BASE64_EXEC="base64 -w0" BASE64_EXEC="base64 -w0"
JQ_EXEC=jq-linux64 JQ_EXEC=jq-linux64

View File

@@ -1,6 +1,6 @@
{ {
"name": "repertory-ui", "name": "repertory-ui",
"version": "1.3.3", "version": "1.3.4",
"private": true, "private": true,
"author": "scott.e.graves@protonmail.com", "author": "scott.e.graves@protonmail.com",
"description": "GUI for Repertory - Repertory allows you to mount Sia, Skynet, and/or ScPrime storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.", "description": "GUI for Repertory - Repertory allows you to mount Sia, Skynet, and/or ScPrime storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.",
@@ -10,21 +10,22 @@
"@fortawesome/react-fontawesome": "^0.1.13", "@fortawesome/react-fontawesome": "^0.1.13",
"@reduxjs/toolkit": "^1.5.0", "@reduxjs/toolkit": "^1.5.0",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"axios": "^0.21.0", "axios": "^0.21.1",
"devtron": "^1.4.0", "devtron": "^1.4.0",
"electron-debug": "^3.1.0", "electron-debug": "^3.2.0",
"electron-log": "^4.3.0", "electron-log": "^4.3.2",
"focus-trap-react": "^8.3.2", "focus-trap-react": "^8.4.2",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"node-cron": "^1.2.1", "node-cron": "1.2.1",
"prop-types": "^15.7.2",
"randomstring": "^1.1.5", "randomstring": "^1.1.5",
"react": "^16.14.0", "react": "16.14.0",
"react-checkbox-tree": "^1.6.0", "react-checkbox-tree": "^1.6.0",
"react-dom": "^16.14.0", "react-dom": "16.14.0",
"react-loader-spinner": "^3.1.14", "react-loader-spinner": "^4.0.0",
"react-redux": "^7.2.2", "react-redux": "^7.2.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"react-tooltip": "^4.2.11", "react-tooltip": "^4.2.15",
"redux": "^4.0.5", "redux": "^4.0.5",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"unzipper": "^0.10.11", "unzipper": "^0.10.11",
@@ -36,6 +37,12 @@
"electron": "5.0.13", "electron": "5.0.13",
"electron-builder": "22.9.1", "electron-builder": "22.9.1",
"electron-webpack": "^2.8.2", "electron-webpack": "^2.8.2",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-jest": "^24.3.2",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.23.0",
"prettier": "2.2.1",
"webpack": "4.44.2", "webpack": "4.44.2",
"webpack-dev-server": "3.11.1" "webpack-dev-server": "3.11.1"
}, },

View File

@@ -1,12 +1,4 @@
const { const { app, BrowserWindow, dialog, ipcMain, Menu, nativeImage, Tray } = require('electron');
app,
BrowserWindow,
dialog,
ipcMain,
Menu,
nativeImage,
Tray
} = require('electron');
const AutoLaunch = require('auto-launch'); const AutoLaunch = require('auto-launch');
const Constants = require('../src/constants'); const Constants = require('../src/constants');
const fs = require('fs'); const fs = require('fs');
@@ -44,12 +36,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' || platform === 'darwin' ? 420 : 400,
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 +80,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 +94,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 +134,12 @@ 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 +148,56 @@ 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 +205,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();
@@ -225,37 +227,46 @@ 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 +288,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 +299,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

@@ -1,60 +1,60 @@
{ {
"Locations": { "Locations": {
"centos7": { "centos7": {
"1.3.3": { "1.3.4": {
"sig": "BL1hmylvFtrY5GAJaZD4m2YAgOgf21N7aCRmQmVQqDpA5tLrLhaCxJaRwQaNTXrNFnwL3YHBXJQf5cJi43dVXI7o+rcltU5QDA2Q7DvzdlaMfL9saBzxLT0D94l017XvczaxhOlypEQIUzFVQYxGzdw3Kx6fKWFySeKgS4l+SB1FTXfeYhfp2ZApOdRB1Y9BzAIN0hATm/UBcBL12FlwCl4+YV6BEIz/07I8VypwxLx68dqGCE/ucTVCf/4D00oGcWEFfrIxBnGzSLFWSyvhvyJwJWvH4VIDIP2KwlhdEs/3p0ETzSl1YbV+bOeUbUGR6Nv2ljF62Y918WOXspiC6N2bDzvpBZkkpKs0xRih4AJQDHR6gs3/43XIqrnTrKMZbZVy2JnEkl3KNGmscLjzS8lus7Wmkn8l0qyOuGY30ef4q60q55VVUw4y+Ox66WbnMYpLpSCY56iNOjw0Kp+rpV7UreLFt5UffQuWWlOttrXs1XJKO9GYv8kbognKAf8gRr7iJWMbZWe8o4++P9ziwt4nd7JlcGzxKinntB4Jn/BHYJnJKGqHhA5j8EJ1QZlu3txruKdcOPV7RpXoFEvOJZ8rji7mUUrlPGM/UJ8JhLcr8P+goKza9dFcX3pFqVHOll4pNOmpPOE+ui+UPSk0I9oCBlEvHSMijywVnwjMh2Yb+VBbuC9Hw5qK2kMZgGXpysa40WrEO5dznJclRSDChIKWVJzdvqnQ2mkR7va+4YAi7Pi/7JnJkO1gcM7O0hGz2bHeG3j7xdUvf0pS8YCcEYojJ97xnBH5UWDYXPBnMsrBMy7mwu/TH8MEGtLiC+9k6THV4Xf9ooS2roHhVskkK5u3xhNFNLxeBVBnQvk7lHynjFbjVNa3URMsD5J5SzQ8O10UhAV8vnpTD70GO25htI1MZuMgV8KPZZ5pOlUMrxqgS14obph8TxMUrD3Wy6SZrwYj/0JTY0lkfhxaXuaecoKDVtCVmiTCcioFryvMymCuz0/0FV8ctlau5Ko3W2W0wmWCVmlDSU5xEybVDegSSnI6bWPQn1SgHUC6DpaWa5LOtCMBgA84AAEMRCMDIcc1EY/VAHz/NeU9UJFESdv/uniL2wcWRq72xPYVwD5fB5YEyFgHx3UXNxhtwMWeThEaOvmHp2snOksM6RSjXVcBLkzK7JQxb25AKEo0rpgHqcVZo+pRpl/6RyXmJWQb1Fl0N6/0fDzJUS3Ji1fAw4O9T2ftrrk1WUD/PsjdaXKRJfWDNl9iWN4gbv+KZAwx+BgF+oMD4b7bOU+cQnkISzT2WD8k/S7wKhrbyMGOWwPYHnebIj0TK0QR0+RWpcP5BggGe7/YpjizvVPGBySjYTcWcig=", "sig": "CQZdxHimuD3li5JcHKJC3zdqNdw18RXFBQ9fgtxXe5FpgPq3bycXrMjmqaJaz25fLPl++e33PSlnzdaC2ede9vrWSfqfB3UG5Vdxxth/YnsqV4YzgGwOed6v9F7YcR2wgts8FpCzy+vokGWu2lI05pCx4ObNgqyvbyGcvGdzgTylhF+MlgSDtXdDV6B1Ba9d+Oj8lh3eA2d7v09ltxavEoiQmrX0ODK9dov9axT3JT/RZTNpAhTSfTT5YUgXmYWYQjZKdwCbFc4k23C1V0SKivB8lQ26Eaaxegwse+Kybar6CzisSkvAlTza9OCo/pxmJ/YedReldyj8pbq7+BweG82JWm8HVl4Y+ppXej69zFtAgcv1QWMYyXmBTfzoKMEUhrH3Brnj3b4ssAG6MKH2Y+lzwhzDYhpNXZ/+jfknFZPaGQ4UgUd1Nf46dPk0Pt+x3tzcweRO6XacfI8e+PNf5YIk+CKxyGjoVJfz1dEAKKg1RL1RXqHPK44MTSrHAIyUPgRNUq6zX23VkfdT4b/IftaIYEEO738wT0nutAtg7RLo2jx6w3hTjDSmh9Wz/szhtS/pP670PycryqH/JaE/5n4lgvSIZN7Inz9WWHbttX54H36usqcEiKr5yoSeOvdIsjWpHguKxasRNbib+Idnhug/Zs8XmshUfwAH3Y+6Wy5dM+IGfuyFpwkfQE2DQmfriyyn2gQf1EJZ6lxuFy1q5vQ9pf3QOaEU72yCu63EEYiXEnPQKgMPkBXxgo6Rp/Nskth0F2XXUYDUPD3lzOJLNMKn3G7ta5EkmZZhrk94pm8X5DpXdxzxFr/lvlWNic5trhWhVtJfci+yxZTI9IiXEfLTck0oYzkpaSxgTvOZ4DklW92xk682F41eNmdo64NwyqeHYS7uvLsJa4aRmRVmsfbY5RuOrDlAmr+yDX9OaISFfhGgByqPlv/5Sm27GFCHFfkvPrUGQWBOFnoRrnr1VZqKr2xRImDUhsP2FNaZJH2E0lBh7P6WmFu2/N1XwJ9w3TX1pwQXGJ/XERSCj7w972VZ8YMj+LHZ4anpZHuY4eL9PAkgwEEpTIqUJuJ2Oj1LGG3GGlT/HrR7tSofybi6+tNsD4egdFZ4HI3+nrPFGbYTQNQzzxXmPnukdP66h4lGGz8bBVT/wezKCK06RrgUKoi+GxcndMnF6JlAzzmiEDh2CXHJsFIr5eDx+pgxbr4Ibz4Mwu9qfeN6fNC+EAkewC1W3f3HoX3oozAT1AobitkT6CV7rHUD4xHWih/7uA+9uuAI2VgxfpfzyvR5H7kd51p63sB1DyvzpB9ssf/E7vxyzQJJhDEOWXUHi0ciMgeElFgJMUI49xzGvJUizV6YmwE=",
"sha256": "03c29d95cb5406f90b076d633fd6a3e2f4a42a5bcf75433099453c0767acc761", "sha256": "ef29a5aad24f9f10cbac3e46b1be334a2f80b184efe0dcc09a5c1fa4526a68f4",
"urls": [ "urls": [
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_linux_x86_64.AppImage"
] ]
} }
}, },
"darwin": { "darwin": {
"1.3.3": { "1.3.4": {
"sha256": "7fbcca92d5d3c728e3663a4cf4035c5d1c45f4cd3a43b5dab605969aa9f95d29", "sig": "BGYvkHV/lP0/6sBdrsO2L3W94oFHduVd8J3DT/SJIXUKRbjLIUGLKwnF27H7gVr1rO1vHQWaNuVaBHQ8guTqh2GAxEFr9w+sFMm0dvnuRndpxcJ4AtVT6j7TiRojPMAPFk9Kd8rNda0jGH9CPG8358K0SNVU61/upi3+zHv4R/KYC47ZK/7R6BWxBUTA1l8/5Q70TE9Kco75j6c8fK8dV6nFfN8rp2mAmxZhmRtV2uFL4OguYhxM6Oba+wxVlq7ywiPjtck4gkYrI9wMvWetK0erI6lwki5YpwEN1tRqDqD2DeKBqsMj5H8kElHMCh+g9Y5DvVIKR1pZdpopgmIQNsfmo/CYgicUvN0ppvyMuLJ/p4PPVIg6ldu6VgjOBXykZIRkZF4TgKsVaDdOpQ6E2Wd4+tERrXwKeP76+3h1RYl4W2NSZ9Lc2wWe3FmK4HKU6jbJ1qy8FsjjxEHs0OJvvYmmEp9bIMtCPZnwf5wR4V2Ht3BScV93mAaWbaykBuWWpXtxiuxw01BZ9w8LAHKJ6BX48wEtz6CAQqF8nc5kDkexsMy2Al2fv3lj+xa5RPjHO/tY4jbUrAn1TCVc98tz+vHbD9TITVkdYcSPNUSMpUANGTvOTQ1CkjOO6pavpMlo96kdLKG+YqK0ERfCQ4YI3V7CjJhEdrkg9ZhLtv7vtyG4WicXBOARaW1OiQFYB48/np/6anKoS2uCDRQdj9vHLyE4C7PwfPKIg+cFCzCMGcvjHrBbGf7PnmSyYFY7PvGcEWylINoWeHquqAGq+N6wTNm+5K/IzmXh+II4dm4f0G15S4gSE/Ux4sIgYADAVXz1uaeARr+EfDOtUjqxAX1Aay1y+1WEFI3L66gszcdpiRsc46gwbYIJ585rVs+FOLNZI6Ak0RawzyZcifCcwaTWGG+kASLyC5XIgayw0hT3qPoZofSasB1RGM3PQ6trz8937RdDK2rQpry01zWsG/Kk+gxI5mZkU2kWgPtUxCPTG6+lLkEY7p9mg/Lo4/8Ze/qlb/xR/9i4+BBqtRRUHJzAsps0K0KicO7+MXB+Sc4hbvXkGFwpFd+JYy/Ry7TVRpiCp8Hdhk9PRtp//Ub1Fun4f7eadTCsbJs7duwKeBhUP0uGP2xo1pyWtFmZLm5ww2g40nEIHy8C5cvadPlN3NTKBZ8lqXqiN6fNtXel+T65vyoVic1FbVYihXSZOmFsJ+7mwDF6ayelAnx9kC0uI0gxpjaZWGPa1ygLWYNkSz4KIO2m5t9NZL6JyVXIRgPvie9rJKAFB2lgxBnkehpQMbU/FgpbzWb0a78OEkAxr4mD7U4WerlVU6jg8xx53tkCSpv35kNwsAOwW7YE+KN8VMW+6j0=",
"sig": "BPM/WKDq+vueZ7YcSI3S+GbisBPsv/OjZPnyxAfHvDQst+Fjrx/9jmTJTTFMtP0xZ1RtrnmSkAwjnyTJRgEPCfe4yWsV52augPp9tvEBVKJgb5KM10h1yN0+DlNB4PMPdE6Ix520mIbQXY72BedPt3qhkom5RN/rQ/ZTHO9qmKwHpvaukh4tUsfsMlGEzjm/Bzch2qdj1pi4CGuug0KxIp23NSgk2ic4EcnVR+aa1lhEpjgE6pGg5ikQ07GR/mFkvr6xAlrEQVXuJ7qGStkXjNuNywSAhT+kUwRr1Wd43s7FEikInLLbewhXStZLG/Q9fVYFxXBoPhk6g5deshGabmNCWt/4JcYYFEq5HWCoaT/86DKQJnLZ3ZMnO/cqlj/7jbi29TmTxvoqdDADp7yRMAcM89LsjDMiFnADlnlsBhUYrM8kjJsd4VGZESviMWmls3kPr3oaZ+OjBQdcYX/bpvrFULFtRxOHYPJHJ9W1IkOd2EGYf/BcxyjRxGqI18oj3a9VaVzyM3IWm3i2Bw0ijj2GQ+p/qeyy3lzTNM25LuUe/PJF9d0wuLisTpvFyW52tNOBLqhrT2wQK8oCo95MqSNKd22vfxJzTcD8PlZSG9ZXNTrmKFbYqJmi6JptUNLVlyn1L8uyJf4S+vL8HAJ1hSqr4iJ/AIF5nMBONzE/4PCP3tathkJ67Yfw0eQbOwl7d+fXSJjZtggFUADLGcwrCLHRclSiW7/9LpL6YJD+h0Ud9ZgLNjYjU/bHL6TCqeAtbSBYG6VNFWN+s73NaBkzC2kx6HnWaa2q4AHAxoix7/LUWhdWh85XGa1UUFWtyxy+hIM3cpacKrSd3u9VdKLHL3GtdfvQjvEE/5UyjH2VzedRVZntjVlBTnVDHq/J4prBAZyA/PSOGtK1x0LmlH2UdT3QEU1WNZaag7vLKWxfPPVvO8zoJG21l8xFWfoAZ/90HA5WRxEsSavSUXzvffmv9O5C/WnWj0tNRsWYqwOYS9l7IcrUDB68V2Rws/sDG56xfgqzdZM4CjM3BBQyarVWr9wyzxAfR0VFOf7GsjF7A1OpQXPiY8pVHg7QPTUSG0Ssse1/leZnU11CEpr/v8JwmHi7LRZ0KSbUowRwPjkzVdDrRjnIQh+2ssphL5mJHNfoxNUFAbvQidN5OjzdUoqEK7H6/OjjHek9mgvJuDDYSHV5cUwgmgPFTwONarrepFThj5YwKGQhYYsCqJLZ+ZunzsjXTJ3VlIF+B0qir370oEWq9dWvaLUYkC6QCHifJ+ZVaPp/pNln7S0zh/dTGO84aKwS0hF7S0uvTTiKRXHTBDmHnmSQuoG8Fku1iqbE9fkScIYPF2wO5N7UPgU86te8/to=", "sha256": "5bb39cebbb0ccb3d010513d8f2aec11d370d78214a40515a4b484a3c65eb58ad",
"urls": [ "urls": [
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_mac.dmg" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_mac.dmg"
] ]
} }
}, },
"solus": { "solus": {
"1.3.3": { "1.3.4": {
"sig": "BL1hmylvFtrY5GAJaZD4m2YAgOgf21N7aCRmQmVQqDpA5tLrLhaCxJaRwQaNTXrNFnwL3YHBXJQf5cJi43dVXI7o+rcltU5QDA2Q7DvzdlaMfL9saBzxLT0D94l017XvczaxhOlypEQIUzFVQYxGzdw3Kx6fKWFySeKgS4l+SB1FTXfeYhfp2ZApOdRB1Y9BzAIN0hATm/UBcBL12FlwCl4+YV6BEIz/07I8VypwxLx68dqGCE/ucTVCf/4D00oGcWEFfrIxBnGzSLFWSyvhvyJwJWvH4VIDIP2KwlhdEs/3p0ETzSl1YbV+bOeUbUGR6Nv2ljF62Y918WOXspiC6N2bDzvpBZkkpKs0xRih4AJQDHR6gs3/43XIqrnTrKMZbZVy2JnEkl3KNGmscLjzS8lus7Wmkn8l0qyOuGY30ef4q60q55VVUw4y+Ox66WbnMYpLpSCY56iNOjw0Kp+rpV7UreLFt5UffQuWWlOttrXs1XJKO9GYv8kbognKAf8gRr7iJWMbZWe8o4++P9ziwt4nd7JlcGzxKinntB4Jn/BHYJnJKGqHhA5j8EJ1QZlu3txruKdcOPV7RpXoFEvOJZ8rji7mUUrlPGM/UJ8JhLcr8P+goKza9dFcX3pFqVHOll4pNOmpPOE+ui+UPSk0I9oCBlEvHSMijywVnwjMh2Yb+VBbuC9Hw5qK2kMZgGXpysa40WrEO5dznJclRSDChIKWVJzdvqnQ2mkR7va+4YAi7Pi/7JnJkO1gcM7O0hGz2bHeG3j7xdUvf0pS8YCcEYojJ97xnBH5UWDYXPBnMsrBMy7mwu/TH8MEGtLiC+9k6THV4Xf9ooS2roHhVskkK5u3xhNFNLxeBVBnQvk7lHynjFbjVNa3URMsD5J5SzQ8O10UhAV8vnpTD70GO25htI1MZuMgV8KPZZ5pOlUMrxqgS14obph8TxMUrD3Wy6SZrwYj/0JTY0lkfhxaXuaecoKDVtCVmiTCcioFryvMymCuz0/0FV8ctlau5Ko3W2W0wmWCVmlDSU5xEybVDegSSnI6bWPQn1SgHUC6DpaWa5LOtCMBgA84AAEMRCMDIcc1EY/VAHz/NeU9UJFESdv/uniL2wcWRq72xPYVwD5fB5YEyFgHx3UXNxhtwMWeThEaOvmHp2snOksM6RSjXVcBLkzK7JQxb25AKEo0rpgHqcVZo+pRpl/6RyXmJWQb1Fl0N6/0fDzJUS3Ji1fAw4O9T2ftrrk1WUD/PsjdaXKRJfWDNl9iWN4gbv+KZAwx+BgF+oMD4b7bOU+cQnkISzT2WD8k/S7wKhrbyMGOWwPYHnebIj0TK0QR0+RWpcP5BggGe7/YpjizvVPGBySjYTcWcig=", "sig": "CQZdxHimuD3li5JcHKJC3zdqNdw18RXFBQ9fgtxXe5FpgPq3bycXrMjmqaJaz25fLPl++e33PSlnzdaC2ede9vrWSfqfB3UG5Vdxxth/YnsqV4YzgGwOed6v9F7YcR2wgts8FpCzy+vokGWu2lI05pCx4ObNgqyvbyGcvGdzgTylhF+MlgSDtXdDV6B1Ba9d+Oj8lh3eA2d7v09ltxavEoiQmrX0ODK9dov9axT3JT/RZTNpAhTSfTT5YUgXmYWYQjZKdwCbFc4k23C1V0SKivB8lQ26Eaaxegwse+Kybar6CzisSkvAlTza9OCo/pxmJ/YedReldyj8pbq7+BweG82JWm8HVl4Y+ppXej69zFtAgcv1QWMYyXmBTfzoKMEUhrH3Brnj3b4ssAG6MKH2Y+lzwhzDYhpNXZ/+jfknFZPaGQ4UgUd1Nf46dPk0Pt+x3tzcweRO6XacfI8e+PNf5YIk+CKxyGjoVJfz1dEAKKg1RL1RXqHPK44MTSrHAIyUPgRNUq6zX23VkfdT4b/IftaIYEEO738wT0nutAtg7RLo2jx6w3hTjDSmh9Wz/szhtS/pP670PycryqH/JaE/5n4lgvSIZN7Inz9WWHbttX54H36usqcEiKr5yoSeOvdIsjWpHguKxasRNbib+Idnhug/Zs8XmshUfwAH3Y+6Wy5dM+IGfuyFpwkfQE2DQmfriyyn2gQf1EJZ6lxuFy1q5vQ9pf3QOaEU72yCu63EEYiXEnPQKgMPkBXxgo6Rp/Nskth0F2XXUYDUPD3lzOJLNMKn3G7ta5EkmZZhrk94pm8X5DpXdxzxFr/lvlWNic5trhWhVtJfci+yxZTI9IiXEfLTck0oYzkpaSxgTvOZ4DklW92xk682F41eNmdo64NwyqeHYS7uvLsJa4aRmRVmsfbY5RuOrDlAmr+yDX9OaISFfhGgByqPlv/5Sm27GFCHFfkvPrUGQWBOFnoRrnr1VZqKr2xRImDUhsP2FNaZJH2E0lBh7P6WmFu2/N1XwJ9w3TX1pwQXGJ/XERSCj7w972VZ8YMj+LHZ4anpZHuY4eL9PAkgwEEpTIqUJuJ2Oj1LGG3GGlT/HrR7tSofybi6+tNsD4egdFZ4HI3+nrPFGbYTQNQzzxXmPnukdP66h4lGGz8bBVT/wezKCK06RrgUKoi+GxcndMnF6JlAzzmiEDh2CXHJsFIr5eDx+pgxbr4Ibz4Mwu9qfeN6fNC+EAkewC1W3f3HoX3oozAT1AobitkT6CV7rHUD4xHWih/7uA+9uuAI2VgxfpfzyvR5H7kd51p63sB1DyvzpB9ssf/E7vxyzQJJhDEOWXUHi0ciMgeElFgJMUI49xzGvJUizV6YmwE=",
"sha256": "03c29d95cb5406f90b076d633fd6a3e2f4a42a5bcf75433099453c0767acc761", "sha256": "ef29a5aad24f9f10cbac3e46b1be334a2f80b184efe0dcc09a5c1fa4526a68f4",
"urls": [ "urls": [
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_linux_x86_64.AppImage"
] ]
} }
}, },
"win32": { "win32": {
"1.3.3": { "1.3.4": {
"sha256": "a86091d453600506cccb25759b62a638c482ce8983d24a5b3ec1def9545ae1c3", "sig": "BbUW6SbAl5pS+WF2kzmy0O8Zw6sdkP1TOP7GS78I+jNLgXzvFodQDsC2dqZclrtcqNvVLIdfHfZeRFYKsrJnEFmZc9Jib3+4rALdhsJY0kRzhgfZkhwydAIuRP/RTXJ4AKh8XgCuxoToPtytslSCgHN5LJmtDQL10naJrvk40n9Pkt0Kjep/8Bj1fc+7WMgtLaKJXiKjGlfax4/2UeMVyqTmkEbRPf9o8w3esfSfRvMwqAN1LnwW4ZbPDLVm1cRh9+BovCKEmVc28LvdtWY0pOios8K5NOI8GcfDmL+a3NsKow1GQPTqEMEa6/z+njdYehVVMeFpA8kI0UKljrQTf4n22BNE1ZzpBOoOCRxcrI4BeG5cy1VhzM7BUm+jaKxY/IuqdLNplXa7VSfRuI1sPVEegmQmDhZCJJXdugVC008UXO8HH731EHUCctMutQTCNLVHg1djyVTtuevQRalXEQMSY+VbAEP1ED3cwfJgwk92ZAVc323IjGNvjeDDlZW5sFUJUYBv94tfy8AWFDk2HnXG5r480fHwrI3ZoZh6cAssiV3OYaLjWUYlQkfWi7fIr43tEIquMxqcuMN+iRfxl+klvQt50xdeE+S/YWMRnixj0h+xSL79f0dryyYofwL924e8U8XSwxGvAPNiADc+VoyRNSqQnPtHn164VOfAPZCaIn8puex9LA5JYEhY9ayB7EW2YVd/Pfp7uI/KAzmngFuKyKG8KVvVE7qF7zSjX222SlweBRLm7f1QPthl0XzVgzzZJ8S1cHPJ//1unOsimQGmPhZDQrnhdFSrtkd2D1kX5dqmTKEx+wiL+Tm8cWFwPvg2nUfWlC9Zytpi9VjpbnczTQLW+JsPB7HOQ4ZYaWaxQATvb3kku3icWv9dY6gWa9Lc9N+gXbGkPV9R+Np0m92m9QA+TDf9HrZwzXLfBjUbfqrMvGhCdznJ9PFflbNQIsDLJeBFdoaHYX88IJIgom60Ef+EjUAhAsvW5A12g+4phENSIg2iv7Ifxc4l8KgtIyjLst9sBdhTDOH3J3Cscd+BxWVLeGjMWoMZ2mU6Bf6ohnO4Ofh9+oqmemHu2rrI3RxOSQF4w9sEv6AtD6C7TG3B+8qFdMic1XkRDxAsNHrwfKOxCytQ89fBH+fzNHRzGys7ADoTH0vm7FR7iADdy83Xt+i+7TK9KvUkR/W5cGgxRHDS1g4SdLetMf/FQ5olbyaW/OiGDtpzipS/Yv3QCMfn4XB3GahMPgLzu0uI9MNXFz9zjwwy0owgzP3Q6+ff3cn8QOIfa0RRLfJgyS71kbcEjHHRehYfuRHNi9/5NMF29irsbAxhWZwncCsoDRJvycwyz6dY+DZHkaDqUv0h8ew=",
"sig": "AeaJxYZ8LpeCAKJEeS0QmfwL894tHmJ2tHi4JRQPoPOzU2fxMSthgOIOLgeTIDuagVk4hbUeZ02Q/t53AQCcrgmyT9xxJy1/2gdkgBN4yLcD76dg9QsSIAT/D3W3oOwzvRu2GRHzKKjD+dzR2HDSbMz6YHsIJDBGqWEcd3PTJ0jREWpQm8slbNflnmMIgumRKzwws93fus4rxXqTTgl/wn01wgSbgrSjncuTRXQUtk63LofkH87g3ivLd3iHVUnCHYQlpKKUuyK1t5Prr7akcFno588k5VGmRYrXljcakobZVeG5ikCFl3+463Eq4Bf1AzMHS6jlSAyEigF10Ys0iD59DnoE2/q2GJekbW0Zjs7qWTQldJyvwa5lhsU5gOxgJYcydhmAu1jEw74jHu/XDFxIpsrfAZfowHTuItsv5kSjJAQleMtDmMnOPs+S+PDhgOTQjtksplnu3aSHdKKE88nkUdWhcddxTFqD1R6IKSBvPZGcRX17n64MnzWqq3yuSV0qAe0CgRWF7Cy48YMeaPk1V+yCkUSh2nvIkjfRc5rRv14Jh2sq03beMxnEC2n+PSV5WA85gWcmkl53iDJSfWATCq6M1rj+PzTwmnjagjnhgDDLolH/g/RWabUEA9ywG9xtJb2M0aCAY7sqeX8I5WEODkfmHTGvYi6E9L7xvpHwmYPxVr2Frs+PbKa8h5C4RLJZGQlia5Rw7i3g0byd9shpbRcrzoGEJmBbkzdMWgSwIVXPD+EqqfiTOdcmQy1JwsTF9zUx7auxXviAddr6HlXXFLt661b47WElRJGXgl1m9qi8eSYRIH0a5wF5XRU+LyCDllUTZhMNNGGP1U3azu3iYsOaYphobn/IVlZBJ4phoafr4D69PbOG+k9B4pTxwHeY7jmszUpiJMgonoftp9TLsLIzDcXBp0vEVoPE0fr1ljOAaQgdtsi0wr0Z2Nxtp5d04d/cYfvfOKx2M/uupdpmU/aBCnv79r9etnnEHhcQtpfCNAZ4567pYAiLiJ4zVEct715hZ6LN57Wx5YWfNT6aCUogWaLx0K5CaL9kv0HyllIoM5z1+7unKwmq4nSO5A1QAh67yCfPTISL2m1SIODmnYUqgBk95oX/AAEk+gaCil0b2A6JGxbI3OcUEyN4BZcWyJRL2QToEmUMNkUuK+aaQl5OTp2wNLFXO1wpqXQAVppDeY2nHqjUY9B2Wku2wFV61T1VZ9sYlGoFYjK7G6PQUsBCTmXAlpzLUjWadgWvKPyRehy4Bzdqwy+BuZ8BEC+txUDnh90jUqd9YCXGsMLEGa6EXijjCBRhqdfQTLZpd3n62YpMvXkfQ6lNnaI4ybqqXwuDpvB5Cvx3tk2VOwc=", "sha256": "8b1e9bd73e11e4f8ef66f417c8735ea48715f41e822d7f77852bf392ac5970c6",
"urls": [ "urls": [
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.3_win.exe" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.4_win.exe"
] ]
} }
} }
}, },
"Versions": { "Versions": {
"centos7": [ "centos7": [
"1.3.3" "1.3.4"
], ],
"darwin": [ "darwin": [
"1.3.3" "1.3.4"
], ],
"linux": [ "linux": [
"unavailable" "unavailable"
], ],
"solus": [ "solus": [
"1.3.3" "1.3.4"
], ],
"unknown": [ "unknown": [
"unavailable" "unavailable"
], ],
"win32": [ "win32": [
"1.3.3" "1.3.4"
] ]
} }
} }

View File

@@ -8,19 +8,3 @@
background-size: cover; background-size: cover;
} }
.AppContainer {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.AppHeader {
height: 28px;
margin-bottom: var(--default_spacing);
box-sizing: border-box;
}
.AppContent {
height: calc(100% - 36px);
box-sizing: border-box;
}

View File

@@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import './App.css'; import './App.css';
import AddEditHost from './containers/AddEditHost/AddEditHost';
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,28 +12,22 @@ 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, saveState } from './redux/actions/common_actions';
displaySelectAppPlatform,
saveState
} 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';
import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon'; import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
import UpgradeUI from './components/UpgradeUI/UpgradeUI'; import UpgradeUI from './components/UpgradeUI/UpgradeUI';
import { import { loadReleases, setDismissUIUpgrade } from './redux/actions/release_version_actions';
loadReleases,
setDismissUIUpgrade
} 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 +54,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 +75,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 +94,51 @@ 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.MissingDependencies.length > 0 && this.props.AllowMount;
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 =
!this.props.RebootRequired && !missingDependencies && !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 +146,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 +166,19 @@ 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 showAddEditHost =
!showDependencies &&
!this.props.DownloadActive &&
!showNewReleases &&
!this.props.RebootRequired &&
!this.props.DisplaySelectAppPlatform &&
!showUpgrade &&
this.props.DisplayAddEditHost;
const showSkynetImport =
!showConfig &&
!showDependencies && !showDependencies &&
!this.props.DownloadActive && !this.props.DownloadActive &&
!showNewReleases && !showNewReleases &&
@@ -167,7 +187,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,97 +197,143 @@ 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 addEditHostDisplay = createModalConditionally(showAddEditHost, <AddEditHost />);
const downloadDisplay = createModalConditionally(this.props.DownloadActive, const pinnedManagerDisplay = createModalConditionally(
<DownloadProgress/>, false, true); showPinnedManager,
const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true); <PinnedManager version={selectedVersion} />
const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true); );
const newReleasesDisplay = createModalConditionally(showNewReleases, <NewReleases/>); const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo />);
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot/>); const dependencyDisplay = createModalConditionally(
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, showDependencies,
<SelectAppPlatform/>); <DependencyList />,
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI/>); false,
const importDisplay = createModalConditionally(showSkynetImport, <SkynetImport this.props.InstallActive
version={selectedVersion}/>); );
const exportDisplay = createModalConditionally(showSkynetExport, <SkynetExport const downloadDisplay = createModalConditionally(
version={selectedVersion}/>) this.props.DownloadActive,
const appBusyDisplay = createModalConditionally(this.props.AppBusy, <DownloadProgress />,
<ApplicationBusy/>, false, true, this.props.AppBusyTransparent); 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 col={0} colSpan={'remain'} row={10} rowSpan={'remain'}>
<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'}}> col={0}
<ReleaseVersionDisplay downloadDisabled={!downloadEnabled} colSpan={'remain'}
version={selectedVersion}/> row={10}
rowSpan={17}
key={'md_' + key++}
dxStyle={{ padding: 'var(--default_spacing)' }}>
<ReleaseVersionDisplay downloadDisabled={!downloadEnabled} version={selectedVersion} />
</Box> </Box>
)); );
mainContent.push(<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++}> row={29}
<MountItems s3Supported={s3Supported} rowSpan={'remain'}
remoteSupported={remoteSupported} colSpan={'remain'}
scPrimeSupported={scPrimeSupported} dxStyle={{ padding: 'var(--default_spacing)' }}
siaSupported={siaSupported} key={'md_' + key++}>
skynetSupported={skynetSupported}/> <MountItems
s3Supported={s3Supported}
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++}> col={0}
<Loading/> colSpan={'remain'}
row={29}
rowSpan={'remain'}
dxStyle={{ padding: 'var(--default_spacing)' }}
key={'md_' + key++}>
<Loading />
</Box> </Box>
)); );
} }
} }
return ( return (
<div className={'App'}> <div className={'App'}>
<div className={'AppContainer'}> <Grid>
<div className={'AppHeader'}> <Box col={0} colSpan={'remain'} row={0} rowSpan={8}>
<Box> <Grid noScroll>
<Grid> <Text
<Text col={0} col={0}
colSpan={'remain'} colSpan={'remain'}
row={0} row={0}
rowSpan={'remain'} rowSpan={8}
text={'Repertory UI v' + this.props.Version} text={'Repertory UI v' + this.props.Version}
textAlign={'center'} textAlign={'center'}
type={'Heading1'}/> type={'Heading1'}
<UpgradeIcon />
available={!missingDependencies && (this.props.UpgradeAvailable || (this.props.NewReleasesAvailable2.length > 0))} <UpgradeIcon
newReleases={!missingDependencies && (!this.props.UpgradeAvailable && (this.props.NewReleasesAvailable2.length > 0))} available={
clicked={this.handleUpgradeIconClicked} !missingDependencies &&
col={dimensions => dimensions.columns - 6} (this.props.UpgradeAvailable || this.props.NewReleasesAvailable2.length > 0)
colSpan={5} }
row={1} newReleases={
rowSpan={remain => remain - 1}/> !missingDependencies &&
</Grid> !this.props.UpgradeAvailable &&
</Box> this.props.NewReleasesAvailable2.length > 0
</div> }
<div className={'AppContent'}> clicked={this.handleUpgradeIconClicked}
{mainContent} col={(dimensions) => dimensions.columns - 6}
</div> colSpan={1}
</div> row={1}
rowSpan={(remain) => remain - 1}
/>
</Grid>
</Box>
{mainContent}
</Grid>
{importDisplay} {importDisplay}
{exportDisplay} {exportDisplay}
{newReleasesDisplay} {newReleasesDisplay}
@@ -275,6 +342,7 @@ class App extends IPCContainer {
{upgradeDisplay} {upgradeDisplay}
{pinnedManagerDisplay} {pinnedManagerDisplay}
{configDisplay} {configDisplay}
{addEditHostDisplay}
{infoDisplay} {infoDisplay}
{confirmDisplay} {confirmDisplay}
{downloadDisplay} {downloadDisplay}
@@ -286,7 +354,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,
@@ -295,6 +363,7 @@ const mapStateToProps = state => {
AppBusyTransparent: state.common.AppBusyTransparent, AppBusyTransparent: state.common.AppBusyTransparent,
AppReady: state.common.AppReady, AppReady: state.common.AppReady,
DismissDependencies: state.install.DismissDependencies, DismissDependencies: state.install.DismissDependencies,
DisplayAddEditHost: state.host.DisplayAddEditHost,
DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayConfirmYesNo: state.common.DisplayConfirmYesNo, DisplayConfirmYesNo: state.common.DisplayConfirmYesNo,
DisplayError: state.error.DisplayError, DisplayError: state.error.DisplayError,
@@ -324,15 +393,15 @@ 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) => dispatch(setDismissNewReleasesAvailable(dismiss)),
setNewReleasesAvailable: items => dispatch(setNewReleasesAvailable(items)), setNewReleasesAvailable: (items) => dispatch(setNewReleasesAvailable(items)),
setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)), setDismissUIUpgrade: (dismiss) => dispatch(setDismissUIUpgrade(dismiss)),
}; };
}; };

View File

@@ -2,22 +2,25 @@ import React from 'react';
import Box from '../UI/Box/Box'; 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';
import PropTypes from 'prop-types';
const ApplicationBusy = ({title}) => { const ApplicationBusy = ({ title }) => {
return ( return (
<Box dxStyle={{padding: 'var(--default_spacing)'}}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<Text <Text text={title || 'Please Wait...'} textAlign={'center'} type={'Heading1'} />
text={title || 'Please Wait...'} <div
textAlign={'center'} style={{
type={'Heading1'}/> paddingLeft: 'calc(50% - 16px)',
<div style={{paddingLeft: 'calc(50% - 16px)', paddingTop: 'var(--default_spacing)'}}> paddingTop: 'var(--default_spacing)',
<Loader color={'var(--heading_text_color)'} }}>
height={32} <Loader color={'var(--heading_text_color)'} height={32} width={32} type="TailSpin" />
width={32}
type='TailSpin'/>
</div> </div>
</Box> </Box>
); );
}; };
ApplicationBusy.propTypes = {
title: PropTypes.string,
};
export default ApplicationBusy; export default ApplicationBusy;

View File

@@ -1,17 +1,10 @@
import React from 'react'; import React from 'react';
import './Dependency.css'; import './Dependency.css';
import {connect} from 'react-redux';
import * as Constants from '../../../constants'; import * as Constants from '../../../constants';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
const mapStateToProps = state => { const Dependency = (props) => {
return {
AllowDownload: (state.download.DownloadType !== Constants.INSTALL_TYPES.Dependency) &&
!state.download.DownloadActive &&
!state.install.InstallActive,
};
};
export default connect(mapStateToProps)(props => {
return ( return (
<div className={'Dependency'}> <div className={'Dependency'}>
<table width="100%"> <table width="100%">
@@ -21,15 +14,40 @@ 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>
</table> </table>
</div> </div>
); );
}); };
const mapStateToProps = (state) => {
return {
AllowDownload:
state.download.DownloadType !== Constants.INSTALL_TYPES.Dependency &&
!state.download.DownloadActive &&
!state.install.InstallActive,
};
};
Dependency.propTypes = {
AllowDownload: PropTypes.bool,
name: PropTypes.string.isRequired,
onDownload: PropTypes.func.isRequired,
};
export default connect(mapStateToProps)(Dependency);

View File

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

View File

@@ -1,14 +1,61 @@
import React from 'react'; import React from 'react';
import './DependencyList.css'; import './DependencyList.css';
import {connect} from 'react-redux';
import * as Constants from '../../constants'; import * as Constants from '../../constants';
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 Dependency from './Dependency/Dependency';
import {extractFileNameFromURL} from '../../utils'; import PropTypes from 'prop-types';
import {setDismissDependencies} from '../../redux/actions/install_actions'; import { connect } from 'react-redux';
import { createDismissDisplay } from '../../utils.jsx';
import { downloadItem } from '../../redux/actions/download_actions';
import { extractFileNameFromURL } from '../../utils.jsx';
import { setDismissDependencies } from '../../redux/actions/install_actions';
const mapStateToProps = state => { const DependencyList = (props) => {
const items = props.MissingDependencies.map((k, i) => {
return (
<Dependency
key={i}
name={k.display}
onDownload={() =>
props.downloadItem(
extractFileNameFromURL(k.download),
Constants.INSTALL_TYPES.Dependency,
k.download,
k.is_winfsp
)
}
/>
);
});
return (
<Box dxStyle={{ width: '300px', height: 'auto', padding: '5px' }}>
{createDismissDisplay(
() => props.setDismissDependencies(true),
!props.AllowDismissDependencies
)}
<div
style={{
width: '100%',
height: 'auto',
paddingBottom: '5px',
boxSizing: 'border-box',
}}>
<h1
style={{
width: '100%',
textAlign: 'center',
color: 'var(--text_color_error)',
}}>
Missing Dependencies
</h1>
</div>
{items}
</Box>
);
};
const mapStateToProps = (state) => {
return { return {
AllowDismissDependencies: state.relver.AllowDismissDependencies, AllowDismissDependencies: state.relver.AllowDismissDependencies,
MissingDependencies: state.install.MissingDependencies, MissingDependencies: state.install.MissingDependencies,
@@ -18,34 +65,15 @@ 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) => dispatch(downloadItem(name, type, url, isWinFSP)),
setDismissDependencies: dismiss => dispatch(setDismissDependencies(dismiss)), setDismissDependencies: (dismiss) => dispatch(setDismissDependencies(dismiss)),
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(props => { DependencyList.propTypes = {
const items = props.MissingDependencies.map((k, i)=> { AllowDismissDependencies: PropTypes.bool,
return ( MissingDependencies: PropTypes.array,
<Dependency key={i} downloadItem: PropTypes.func.isRequired,
name={k.display} setDismissDependencies: PropTypes.func.isRequired,
onDownload={()=>props.downloadItem(extractFileNameFromURL(k.download), Constants.INSTALL_TYPES.Dependency, k.download, k.is_winfsp)}/> };
);
});
const dismissDisplay = ( export default connect(mapStateToProps, mapDispatchToProps)(DependencyList);
<div style={{float: 'right', margin: 0, paddingRight: '4px', boxSizing: 'border-box', display: 'block'}}>
<a href={'#'}
onClick={props.AllowDismissDependencies ? () => props.setDismissDependencies(true) : e => e.preventDefault()}
style={{cursor: props.AllowDismissDependencies ? 'pointer' : 'no-drop'}}>X</a>
</div>
);
return (
<Box dxStyle={{width: '300px', height: 'auto', padding: '5px'}}>
{dismissDisplay}
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}>
<h1 style={{width: '100%', textAlign: 'center', color: 'var(--text_color_error)'}}>Missing Dependencies</h1>
</div>
{items}
</Box>
);
});

View File

@@ -1,9 +1,29 @@
import Box from '../UI/Box/Box'; import Box from '../UI/Box/Box';
import {connect} from 'react-redux';
import React from 'react';
import './DownloadProgress.css'; import './DownloadProgress.css';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
const mapStateToProps = state => { const DownloadProgress = (props) => {
const width = props.Platform === 'linux' ? '480px' : '380px';
return (
<Box dxStyle={{ width: width, height: 'auto', padding: '5px' }}>
<div style={{ width: '100%', height: 'auto' }}>
<h1 style={{ width: '100%', textAlign: 'center' }}>
{'Downloading ' + props.DownloadName}
</h1>
</div>
<progress
max={100.0}
id={'download_progress'}
style={{ width: '100%' }}
value={props.DownloadProgress}
/>
</Box>
);
};
const mapStateToProps = (state) => {
return { return {
DownloadName: state.download.DownloadName, DownloadName: state.download.DownloadName,
DownloadProgress: state.download.DownloadProgress, DownloadProgress: state.download.DownloadProgress,
@@ -11,15 +31,10 @@ const mapStateToProps = state => {
}; };
}; };
export default connect(mapStateToProps)(props => { DownloadProgress.propTypes = {
const width = props.Platform === 'linux' ? '480px' : '380px'; DownloadName: PropTypes.string.isRequired,
return ( DownloadProgress: PropTypes.number,
<Box dxStyle={{width: width, height: 'auto', padding: '5px'}}> Platform: PropTypes.string.isRequired,
<div style={{width: '100%', height: 'auto'}}> };
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.DownloadName}</h1>
</div> export default connect(mapStateToProps)(DownloadProgress);
<progress max={100.0} id={'download_progress'}
style={{width: '100%'}}
value={props.DownloadProgress}/>
</Box>);
});

View File

@@ -1,25 +1,14 @@
import React from 'react'; import React from 'react';
import {dismissError} from '../../redux/actions/error_actions'; import './ErrorDetails.css';
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 PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { dismissError } from '../../redux/actions/error_actions';
const mapStateToProps = state => { const ErrorDetails = (props) => {
return {
ErrorMessage: state.error.ErrorStack.length > 0 ? state.error.ErrorStack[0] : '',
};
};
const mapDispatchToProps = dispatch => {
return {
dismissError: () => dispatch(dismissError()),
};
};
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>
@@ -27,4 +16,23 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
<Button clicked={props.dismissError}>Dismiss</Button> <Button clicked={props.dismissError}>Dismiss</Button>
</Box> </Box>
); );
}); };
const mapStateToProps = (state) => {
return {
ErrorMessage: state.error.ErrorStack.length > 0 ? state.error.ErrorStack[0] : '',
};
};
const mapDispatchToProps = (dispatch) => {
return {
dismissError: () => dispatch(dismissError()),
};
};
ErrorDetails.propTypes = {
ErrorMessage: PropTypes.string.isRequired,
dismissError: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(ErrorDetails);

View File

@@ -17,3 +17,9 @@
white-space: pre; white-space: pre;
hyphens: none; hyphens: none;
} }
.InfoButtonOwner {
display: flex;
flex-direction: row;
justify-content: flex-end;
}

View File

@@ -1,23 +1,13 @@
import React from 'react'; import React from 'react';
import {dismissInfo} from '../../redux/actions/error_actions'; import './InfoDetails.css';
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 PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { dismissInfo, notifyError } from '../../redux/actions/error_actions';
import { promptLocationAndSaveFile } from '../../utils';
const mapStateToProps = state => { const InfoDetails = (props) => {
return {
InfoMessage: state.error.InfoStack.length > 0 ? state.error.InfoStack[0] : '',
};
};
const mapDispatchToProps = dispatch => {
return {
dismissInfo: () => dispatch(dismissInfo()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
let msg = props.InfoMessage.message; let msg = props.InfoMessage.message;
const classes = ['InfoDetailsContent']; const classes = ['InfoDetailsContent'];
@@ -37,7 +27,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,22 +36,65 @@ 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} onChange={() => {}}
className={'SkynetImportTextArea'} value={msg}
onClick={e => scrollToTop(e.target)}/> className={'SkynetImportTextArea'}
) : ( 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> {props.InfoMessage.saveToFile ? (
<div className={'InfoButtonOwner'}>
<Button clicked={props.dismissInfo}>Dismiss</Button>
<Button
buttonStyles={{ marginLeft: 'var(--default_spacing)' }}
clicked={() => {
if (
promptLocationAndSaveFile(
props.InfoMessage.fileName + '.' + props.InfoMessage.extension,
msg,
props.notifyError
)
) {
props.dismissInfo();
}
}}>
Save...
</Button>
</div>
) : (
<Button clicked={props.dismissInfo}>Dismiss</Button>
)}
</Box> </Box>
); );
}); };
const mapStateToProps = (state) => {
return {
InfoMessage: state.error.InfoStack.length > 0 ? state.error.InfoStack[0] : '',
};
};
const mapDispatchToProps = (dispatch) => {
return {
notifyError: (msg) => dispatch(notifyError(msg)),
dismissInfo: () => dispatch(dismissInfo()),
};
};
InfoDetails.propTypes = {
InfoMessage: PropTypes.object.isRequired,
dismissInfo: PropTypes.func.isRequired,
notifyError: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(InfoDetails);

View File

@@ -1,74 +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 {installReleaseByVersion} from '../../../redux/actions/install_actions';
const mapStateToProps = state => {
return {
AllowInstall: state.install.MissingDependencies.length === 0,
};
};
const mapDispatchToProps = dispatch => {
return {
installReleaseByVersion: (release, version) => dispatch(installReleaseByVersion(release, version)),
notifyError: msg => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(({
AllowInstall,
dismiss,
release,
lastItem,
notifyError,
notifyInfo,
installReleaseByVersion
}) => {
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);
}
};
// TODO Switch to activate
/*const installReleaseVersion = () => {
dismiss();
installReleaseByVersion(release.Release, release.Version);
};
{AllowInstall ?
<Button buttonStyles={{width: '100%'}}
clicked={installReleaseVersion}>Install</Button> : null}*/
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%">
</td>
</tr>
{lastItem ? null : <tr style={{height: 'var(--default_spacing)'}}/>}
</tbody>
</table>
</div>
);
});

View File

@@ -0,0 +1,98 @@
import React from 'react';
import * as Constants from '../../../constants';
import Button from '../../UI/Button/Button';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
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 NewRelease = ({
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>
);
};
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)),
};
};
NewRelease.propTypes = {
ActiveRelease: PropTypes.number.isRequired,
ActiveVersion: PropTypes.number.isRequired,
dismiss: PropTypes.func.isRequired,
lastItem: PropTypes.bool.isRequired,
notifyError: PropTypes.func.isRequired,
notifyInfo: PropTypes.func.isRequired,
release: PropTypes.object.isRequired,
setActiveRelease: PropTypes.func.isRequired,
unmountAll: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(NewRelease);

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,48 @@
import React from 'react';
import './NewReleases.css';
import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import NewRelease from './NewRelease/NewRelease.jsx';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { setDismissNewReleasesAvailable } from '../../redux/actions/release_version_actions';
const NewReleases = (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>
);
};
const mapStateToProps = (state) => {
return {
NewReleasesAvailable: state.relver.NewReleasesAvailable,
};
};
const mapDispatchToProps = (dispatch) => {
return {
dismissNewReleasesAvailable: () => dispatch(setDismissNewReleasesAvailable(true)),
};
};
NewReleases.propTypes = {
NewReleasesAvailable: PropTypes.array.isRequired,
dismissNewReleasesAvailable: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(NewReleases);

View File

@@ -1,24 +1,31 @@
import React from 'react'; import React from 'react';
import './Reboot.css'; import './Reboot.css';
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 PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { rebootSystem } from '../../redux/actions/common_actions';
const mapDispatchToProps = dispatch => { const Reboot = (props) => {
return (
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)' }}>
<h1 className={'RebootHeading'}>Reboot System</h1>
<div className={'RebootContent'}>
<p>Repertory requires a system reboot to continue.</p>
</div>
<Button clicked={() => props.rebootSystem()}>Reboot Now</Button>
</Box>
);
};
const mapDispatchToProps = (dispatch) => {
return { return {
rebootSystem: () => dispatch(rebootSystem()), rebootSystem: () => dispatch(rebootSystem()),
}; };
}; };
export default connect(null, mapDispatchToProps)(props => { Reboot.propTypes = {
return ( rebootSystem: PropTypes.func.isRequired,
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}> };
<h1 className={'RebootHeading'}>Reboot System</h1>
<div className={'RebootContent'}> export default connect(null, mapDispatchToProps)(Reboot);
<p>Repertory requires a system reboot to continue.</p>
</div>
<Button clicked={()=>props.rebootSystem()}>Reboot Now</Button>
</Box>
);
});

View File

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

View File

@@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import './Box.css'; import './Box.css';
import PropTypes from 'prop-types';
const Box = props => { const Box = (props) => {
const styleList = []; const styleList = [];
styleList.push('Box'); styleList.push('Box');
if (props.dxDark) { if (props.dxDark) {
@@ -17,13 +18,20 @@ const Box = props => {
} }
return ( return (
<div <div onClick={props.clicked} className={styleList.join(' ')} style={{ ...props.dxStyle }}>
onClick={props.clicked}
className={styleList.join(' ')}
style={{...props.dxStyle}}>
{props.children} {props.children}
</div> </div>
); );
}; };
Box.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
clicked: PropTypes.func,
dxDark: PropTypes.bool,
dxFadeIn: PropTypes.bool,
dxSlideOut: PropTypes.bool,
dxSlideOutTop: PropTypes.bool,
dxStyle: PropTypes.object,
};
export default Box; export default Box;

View File

@@ -1,14 +1,26 @@
import React from 'react'; import React from 'react';
import './Button.css'; import './Button.css';
import PropTypes from 'prop-types';
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>
); );
}; };
Button.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]),
autoFocus: PropTypes.bool,
buttonStyles: PropTypes.object,
clicked: PropTypes.func,
disabled: PropTypes.bool,
};
export default Button; export default Button;

View File

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

View File

@@ -1,19 +1,31 @@
import React from 'react'; import React from 'react';
import './CheckBox.css'; import './CheckBox.css';
import PropTypes from 'prop-types';
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>
); );
}; };
CheckBox.propTypes = {
autoFocus: PropTypes.bool,
checked: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
disabled: PropTypes.bool,
label: PropTypes.string,
changed: PropTypes.func,
};
export default CheckBox; export default CheckBox;

View File

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

View File

@@ -1,10 +1,13 @@
import React from 'react'; import React from 'react';
import './DropDown.css'; import './DropDown.css';
import PropTypes from 'prop-types';
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>
); );
}); });
@@ -20,7 +23,16 @@ const DropDown = props => {
</select> </select>
</div> </div>
); );
};
DropDown.propTypes = {
alt: PropTypes.bool,
auto: PropTypes.bool,
autoFocus: PropTypes.bool,
changed: PropTypes.func,
disabled: PropTypes.bool,
items: PropTypes.array,
selected: PropTypes.string,
}; };
export default DropDown; export default DropDown;

View File

@@ -1,23 +1,29 @@
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';
import PropTypes from 'prop-types';
const DEFAULT_GRID_SIZE = 4; const DEFAULT_GRID_SIZE = 4;
export default class Grid extends Component { class Grid extends Component {
constructor(props) {
super(props);
this.sizeRef = React.createRef();
}
resizeTimeout; resizeTimeout;
state = { state = {
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 +35,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;
@@ -40,10 +46,10 @@ export default class Grid extends Component {
}; };
getSize = () => { getSize = () => {
const elem = this.refs.GridOwner; const elem = this.sizeRef.current;
return { return {
height: elem ? elem.offsetHeight : 0, height: elem ? elem.offsetHeight : 0,
width: elem ? elem.offsetWidth : 0 width: elem ? elem.offsetWidth : 0,
}; };
}; };
@@ -54,15 +60,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 +85,30 @@ 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 ? (rowSpan === 'remain' ? dimensions.rows - row : rowSpan) : null;
colSpan = colSpan ? (colSpan === 'remain' ? dimensions.columns - col : colSpan) : null; colSpan = colSpan ? (colSpan === 'remain' ? dimensions.columns - col : colSpan) : null;
return ( return (
<GridComponent row={row} <GridComponent row={row} col={col} rowSpan={rowSpan} colSpan={colSpan} key={'gc_' + i}>
col={col}
rowSpan={rowSpan}
colSpan={colSpan}
key={'gc_' + i}>
{child} {child}
</GridComponent> </GridComponent>
); );
@@ -123,7 +125,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 +134,19 @@ export default class Grid extends Component {
} }
return ( return (
<div <div ref={this.sizeRef} className={'GridOwner'}>
ref='GridOwner'
className={'GridOwner'}>
<div className={'Grid'} {...style}> <div className={'Grid'} {...style}>
{children} {children}
</div> </div>
</div> </div>
) );
} }
}
Grid.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]),
gridSize: PropTypes.number,
noScroll: PropTypes.bool,
}; };
export default Grid;

View File

@@ -1,14 +1,15 @@
import React from 'react'; import React from 'react';
import './GridComponent.css'; import './GridComponent.css';
import PropTypes from 'prop-types';
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 +17,14 @@ const GridComponent = props => {
{props.children} {props.children}
</div> </div>
); );
};
GridComponent.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
col: PropTypes.number.isRequired,
colSpan: PropTypes.number,
row: PropTypes.number.isRequired,
rowSpan: PropTypes.number,
}; };
export default GridComponent; export default GridComponent;

View File

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

View File

@@ -1,18 +1,15 @@
import React from 'react'; import React from 'react';
import './Loading.css' import './Loading.css';
import Loader from 'react-loader-spinner'; import Loader from 'react-loader-spinner';
const Loading = () => { const Loading = () => {
return ( return (
<div <div className={'Loading'}>
className={'Loading'}>
<div className={'LoadingContent'}> <div className={'LoadingContent'}>
<Loader color={'var(--heading_text_color)'} <Loader color={'var(--heading_text_color)'} height={28} width={28} type="ThreeDots" />
height={28}
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';
import PropTypes from 'prop-types';
const Modal = props => { const Modal = (props) => {
let modalStyles = []; let modalStyles = [];
let contentStyles = []; let contentStyles = [];
modalStyles.push('Modal'); modalStyles.push('Modal');
@@ -19,15 +19,19 @@ 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>
); );
}; };
Modal.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
clicked: PropTypes.func,
critical: PropTypes.bool,
disableFocusTrap: PropTypes.bool,
transparent: PropTypes.bool,
};
export default Modal; export default Modal;

View File

@@ -1,11 +1,16 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
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>
) );
};
RootElem.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
}; };
export default RootElem; export default RootElem;

View File

@@ -1,28 +1,34 @@
import React from 'react';
import './Text.css'; import './Text.css';
import PropTypes from 'prop-types';
import React from 'react';
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>); Text.propTypes = {
noOwner: PropTypes.bool,
style: PropTypes.object,
text: PropTypes.string,
textAlign: PropTypes.string,
type: PropTypes.string,
}; };
export default Text; export default Text;

View File

@@ -1,10 +1,11 @@
import React from 'react';
import './UpgradeIcon.css'; import './UpgradeIcon.css';
import PropTypes from 'prop-types';
import React from 'react';
import ReactTooltip from 'react-tooltip'; import ReactTooltip from 'react-tooltip';
import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
const UpgradeIcon = props => { const UpgradeIcon = (props) => {
const styles = ['UpgradeIcon']; const styles = ['UpgradeIcon'];
let placement = 'left'; let placement = 'left';
let toolTipText = 'UI Upgrade Available'; let toolTipText = 'UI Upgrade Available';
@@ -17,21 +18,25 @@ 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; UpgradeIcon.propTypes = {
available: PropTypes.bool,
clicked: PropTypes.func,
newReleases: PropTypes.bool,
release: PropTypes.bool,
}; };
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 Box from '../UI/Box/Box';
import * as Constants from '../../constants';
import React from 'react';
import './UpgradeUI.css'; import './UpgradeUI.css';
import {setDismissUIUpgrade} from '../../redux/actions/release_version_actions'; import * as Constants from '../../constants';
import {downloadItem} from '../../redux/actions/download_actions'; import Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import PropTypes from 'prop-types';
import React from 'react';
import { downloadItem } from '../../redux/actions/download_actions';
import { setDismissUIUpgrade } from '../../redux/actions/release_version_actions';
const mapStateToProps = state => { const UpgradeUI = (props) => {
return {
Platform: state.common.Platform,
UpgradeData: state.relver.UpgradeData,
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 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'
: 'repertory-ui_' + props.UpgradeVersion + '_linux_x86_64.AppImage';
props.downloadItem(name, Constants.INSTALL_TYPES.Upgrade, props.UpgradeData.urls); 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>
}); );
};
const mapStateToProps = (state) => {
return {
Platform: state.common.Platform,
UpgradeData: state.relver.UpgradeData,
UpgradeVersion: state.relver.UpgradeVersion,
};
};
const mapDispatchToProps = (dispatch) => {
return {
downloadItem: (name, type, urls) => dispatch(downloadItem(name, type, urls)),
setDismissUIUpgrade: (dismiss) => dispatch(setDismissUIUpgrade(dismiss)),
};
};
UpgradeUI.propTypes = {
Platform: PropTypes.string.isRequired,
UpgradeData: PropTypes.object.isRequired,
UpgradeVersion: PropTypes.string.isRequired,
downloadItem: PropTypes.func.isRequired,
setDismissUIUpgrade: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(UpgradeUI);

View File

@@ -1,41 +1,57 @@
import {connect} from 'react-redux';
import Button from '../UI/Button/Button';
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 Box from '../UI/Box/Box';
import Button from '../UI/Button/Button';
import PropTypes from 'prop-types';
import { confirmYesNoAction } from '../../redux/actions/common_actions';
import { connect } from 'react-redux';
const mapStateToProps = state => { const YesNo = (props) => {
return {
Title: state.common.ConfirmTitle,
}
};
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 buttonStyles={{ width: '100%' }} clicked={() => props.confirm(true)}>
clicked={() => props.hideConfirmYesNo(true)}>Yes</Button> Yes
</Button>
</td> </td>
<td width="50%"> <td width="50%">
<Button buttonStyles={{width: '100%'}} <Button buttonStyles={{ width: '100%' }} clicked={() => props.confirm(false)}>
clicked={() => props.hideConfirmYesNo(false)}>No</Button> No
</Button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</Box>); </Box>
}); );
};
const mapStateToProps = (state) => {
return {
Title: state.common.ConfirmTitle,
};
};
const mapDispatchToProps = (dispatch) => {
return {
confirm: (confirmed) => dispatch(confirmYesNoAction.complete(confirmed)),
};
};
YesNo.propTypes = {
Title: PropTypes.string.isRequired,
confirm: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(YesNo);

View File

@@ -1,30 +1,30 @@
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' +
'XH6aKiUwX4olEVKSylRUQw8nVckZGVWXzLDlgpzDrLHC8J8qHzFt7eCqOdiqsxhZ\n' + 'XH6aKiUwX4olEVKSylRUQw8nVckZGVWXzLDlgpzDrLHC8J8qHzFt7eCqOdiqsxhZ\n' +
'x1U5LtugxwSWncTZ7vlKl0DuC/AWB7SuDi7bGRMSVp2n+MnD1VLKlsCclHXjIciE\n' + 'x1U5LtugxwSWncTZ7vlKl0DuC/AWB7SuDi7bGRMSVp2n+MnD1VLKlsCclHXjIciE\n' +
'W29n3G3lJ/sOta2sxqLd0j1XBQddrFXl5b609sIY81ocHqu8P2hRu5CpqJ/sGZC5\n' + 'W29n3G3lJ/sOta2sxqLd0j1XBQddrFXl5b609sIY81ocHqu8P2hRu5CpqJ/sGZC5\n' +
'mMH3segHBkRj0xJcfOxceRLj1a+ULIIR3xL/3f8s5Id25TDo/nqBoCvu5PeCpo6L\n' + 'mMH3segHBkRj0xJcfOxceRLj1a+ULIIR3xL/3f8s5Id25TDo/nqBoCvu5PeCpo6L\n' +
'9wIDAQAB\n' + '9wIDAQAB\n' +
'-----END PUBLIC KEY-----'; '-----END PUBLIC KEY-----';
const _REPERTORY_BRANCH = 'master'; const _REPERTORY_BRANCH = 'master';
const _REPERTORY_UI_BRANCH = 'master'; const _REPERTORY_UI_BRANCH = 'master';
@@ -32,19 +32,15 @@ const _REPERTORY_UI_BRANCH = 'master';
exports.REPERTORY_BRANCH = _REPERTORY_BRANCH; exports.REPERTORY_BRANCH = _REPERTORY_BRANCH;
exports.REPERTORY_UI_BRANCH = _REPERTORY_UI_BRANCH; exports.REPERTORY_UI_BRANCH = _REPERTORY_UI_BRANCH;
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + exports.RELEASES_URL =
_REPERTORY_BRANCH + '/releases_1.3.json'; 'https://bitbucket.org/blockstorage/repertory/raw/' + _REPERTORY_BRANCH + '/releases_1.3.json';
exports.UI_RELEASES_URL = exports.UI_RELEASES_URL =
'https://bitbucket.org/blockstorage/repertory-ui/raw/' + 'https://bitbucket.org/blockstorage/repertory-ui/raw/' + _REPERTORY_UI_BRANCH + '/releases.json';
_REPERTORY_UI_BRANCH + '/releases.json';
exports.LINUX_DETECT_SCRIPT_URL = exports.LINUX_DETECT_SCRIPT_URL =
'https://bitbucket.org/blockstorage/repertory/raw/' + _REPERTORY_BRANCH + 'https://bitbucket.org/blockstorage/repertory/raw/' + _REPERTORY_BRANCH + '/detect_linux2.sh';
'/detect_linux2.sh';
exports.LINUX_SELECTABLE_PLATFORMS = [ exports.LINUX_SELECTABLE_PLATFORMS = ['centos7'];
'centos7',
];
exports.WINFSP_VERSION_NAMES = [ exports.WINFSP_VERSION_NAMES = [
'WinFsp 2019', 'WinFsp 2019',
@@ -61,6 +57,7 @@ exports.WINFSP_VERSION_NAMES = [
'WinFsp 2020.0', 'WinFsp 2020.0',
'WinFsp 2020.1', 'WinFsp 2020.1',
'WinFsp 2020.2', 'WinFsp 2020.2',
'WinFsp 2021',
'WinFsp 2021 B1', 'WinFsp 2021 B1',
'WinFsp 2021 B2', 'WinFsp 2021 B2',
'WinFsp 2021 Beta1', 'WinFsp 2021 Beta1',
@@ -68,58 +65,55 @@ 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 = [ const _S3_CUSTOM_PROVIDER = 'Custom...';
'Filebase', exports.S3_CUSTOM_PROVIDER = _S3_CUSTOM_PROVIDER;
]; exports.S3_PROVIDER_LIST = ['Filebase', _S3_CUSTOM_PROVIDER];
const _S3_CUSTOM_REGION = 'Custom...';
exports.S3_CUSTOM_REGION = _S3_CUSTOM_REGION;
exports.S3_REGION_PROVIDER_REGION = [ exports.S3_REGION_PROVIDER_REGION = [
'us-east-1', ['us-east-1', _S3_CUSTOM_REGION],
['any', _S3_CUSTOM_REGION],
]; ];
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';
exports.IPC_Browse_File = 'browse_file';
exports.IPC_Select_File = 'select_file';
exports.IPC_Check_Daemon_Version = 'check_daemon_version'; exports.IPC_Check_Daemon_Version = 'check_daemon_version';
exports.IPC_Check_Daemon_Version_Reply = 'check_daemon_version_reply'; exports.IPC_Check_Daemon_Version_Reply = 'check_daemon_version_reply';
@@ -185,11 +179,14 @@ exports.IPC_Install_Upgrade_Reply = 'install_upgrade_reply';
exports.IPC_Mount_Drive = 'mount_drive'; exports.IPC_Mount_Drive = 'mount_drive';
exports.IPC_Mount_Drive_Reply = 'mount_drive_reply'; exports.IPC_Mount_Drive_Reply = 'mount_drive_reply';
exports.IPC_Read_File = 'read_file';
exports.IPC_Remove_Mount = 'remove_mount'; exports.IPC_Remove_Mount = 'remove_mount';
exports.IPC_Remove_Mount_Reply = 'remove_mount_reply'; exports.IPC_Remove_Mount_Reply = 'remove_mount_reply';
exports.IPC_Reboot_System = 'reboot_system'; exports.IPC_Reboot_System = 'reboot_system';
exports.IPC_Save_File = 'save_file';
exports.IPC_Save_State = 'save_state'; exports.IPC_Save_State = 'save_state';
exports.IPC_Set_Pinned = 'set_pinned'; exports.IPC_Set_Pinned = 'set_pinned';
@@ -203,6 +200,9 @@ exports.IPC_Set_Linux_AppPlatform = 'IPC_Set_Linux_AppPlatform';
exports.IPC_Shutdown = 'shutdown'; exports.IPC_Shutdown = 'shutdown';
exports.IPC_Skynet_Test_Logon = 'skynet_test_logon';
exports.IPC_Skynet_Test_Logon_Reply = 'skynet_test_logon_reply';
exports.IPC_Test_Release = 'test_release'; exports.IPC_Test_Release = 'test_release';
exports.IPC_Test_Release_Reply = 'test_release_reply'; exports.IPC_Test_Release_Reply = 'test_release_reply';

View File

@@ -0,0 +1,291 @@
import React from 'react';
import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button';
import DropDown from '../../components/UI/DropDown/DropDown';
import IPCContainer from '../IPCContainer/IPCContainer';
import PropTypes from 'prop-types';
import Text from '../../components/UI/Text/Text';
import { addEditHostAction } from '../../redux/actions/host_actions';
import { connect } from 'react-redux';
import { createDismissDisplay } from '../../utils.jsx';
import { notifyApplicationBusy } from '../../redux/actions/common_actions';
import { notifyError, notifyInfo } from '../../redux/actions/error_actions';
const Constants = require('../../constants');
class AddEditHost extends IPCContainer {
state = {
AgentString: '',
ApiPassword: '',
ApiPort: 443,
AuthPassword: '',
AuthURL: '',
AuthUser: '',
HostNameOrIp: '',
Path: '',
Protocol: 'https',
TimeoutMs: 60000,
};
componentDidMount() {
this.setRequestHandler(Constants.IPC_Skynet_Test_Logon_Reply, this.onSkynetTestLogonReply);
if (this.props.HostData) {
this.setState({ ...this.state, ...this.props.HostData });
}
}
componentWillUnmount() {
super.componentWillUnmount();
}
handleSave = () => {
if (this.state.HostNameOrIp.trim().length === 0) {
this.props.notifyError('Host / IP cannot be empty');
return;
}
if (this.state.HostNameOrIp.trim().indexOf('/') >= 0) {
this.props.notifyError(`Host / IP cannot be contain '/'`);
return;
}
if (this.state.HostNameOrIp.trim().indexOf(':') >= 0) {
this.props.notifyError(`Host / IP cannot be contain ':'`);
return;
}
if (
this.props.HostList.find(
(i) =>
(i.HostNameOrIp || '').trim() === this.state.HostNameOrIp &&
i.Protocol === this.state.Protocol &&
i.ApiPort == this.state.ApiPort
)
) {
this.props.notifyError(`Portal already exists`);
return;
}
this.props.completeAddEditHost(this.state);
};
handleTestLogon = () => {
try {
this.props.notifyApplicationBusy(true);
this.sendRequest(Constants.IPC_Skynet_Test_Logon, {
Version: this.props.InstalledVersion,
AuthURL: this.state.AuthURL,
AuthUser: this.state.AuthUser,
AuthPassword: this.state.AuthPassword,
});
} catch (e) {
this.props.notifyApplicationBusy(false);
this.props.notifyError(e);
}
};
onSkynetTestLogonReply = (_, arg) => {
this.props.notifyApplicationBusy(false);
if (arg.data.Success) {
this.props.notifyInfo('Logon was successful!');
} else {
this.props.notifyError(arg.data.Error);
}
};
render() {
const allowTestLogon = this.state.AuthURL && this.state.AuthUser;
return (
<Box dxDark dxStyle={{ width: '430px', height: 'auto', padding: '5px' }}>
{createDismissDisplay(this.props.Close)}
<div
style={{
width: '100%',
height: 'auto',
paddingBottom: '5px',
boxSizing: 'border-box',
}}>
<h1
style={{
width: '100%',
textAlign: 'center',
color: 'var(--text_color_error)',
}}>
Portal Settings
</h1>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Host / IP'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ HostNameOrIp: e.target.value.trim() })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.HostNameOrIp}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Protocol'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Port'} textAlign={'left'} type={'Heading2'} />
<div style={{ paddingLeft: 'var(--default_spacing)' }} />
<Text text={'Timeout (ms)'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<DropDown
changed={(e) => this.setState({ Protocol: e.target.value })}
items={['https', 'http']}
selected={this.state.Protocol}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ ApiPort: parseInt(e.target.value) })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'number'}
min={1}
max={65535}
value={this.state.ApiPort}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ TimeoutMs: parseInt(e.target.value) })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'number'}
min={1000}
step={1000}
value={this.state.TimeoutMs}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'Agent String (optional)'} textAlign={'left'} type={'Heading2'} />
<div style={{ width: 'var(--default_spacing)' }} />
<Text text={'API Key (optional)'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ AgentString: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.AgentString}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ ApiPassword: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.ApiPassword}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text
noOwner
text={'Authentication URL (premium)'}
textAlign={'left'}
type={'Heading2'}
style={{ marginRight: 'auto' }}
/>
{allowTestLogon ? (
<a
href={'#'}
onClick={(e) => {
this.handleTestLogon();
e.preventDefault();
}}>
<u>test logon</u>
</a>
) : null}
</div>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<input
onChange={(e) => this.setState({ AuthURL: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.AuthURL}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Text text={'User Name (premium)'} textAlign={'left'} type={'Heading2'} />
<div style={{ width: 'var(--default_spacing)' }} />
<Text text={'Password (premium)'} textAlign={'left'} type={'Heading2'} />
</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<input
onChange={(e) => this.setState({ AuthUser: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.AuthUser}
/>
<div style={{ width: 'var(--default_spacing)' }} />
<input
onChange={(e) => this.setState({ AuthPassword: e.target.value })}
className={'ConfigurationItemInput'}
style={{ flex: '1' }}
type={'text'}
value={this.state.AuthPassword}
/>
</div>
<div style={{ height: 'var(--default_spacing)' }} />
<p>
<b>
{'Portal URL: ' +
this.state.Protocol +
'://' +
this.state.HostNameOrIp +
((this.state.Protocol === 'http' && this.state.ApiPort != 80) ||
(this.state.Protocol === 'https' && this.state.ApiPort != 443)
? ':' + this.state.ApiPort.toString()
: '')}
</b>
</p>
<div style={{ height: 'var(--default_spacing)' }} />
</div>
<Button clicked={this.handleSave}>Save</Button>
</Box>
);
}
}
const mapStateToProps = (state) => {
return {
HostData: state.host.HostData,
HostList: state.host.HostList,
InstalledVersion: state.relver.InstalledVersion,
};
};
const mapDispatchToProps = (dispatch) => {
return {
Close: () => dispatch(addEditHostAction.complete(false)),
completeAddEditHost: (host_data) => dispatch(addEditHostAction.complete(true, { host_data })),
notifyApplicationBusy: (busy) => dispatch(notifyApplicationBusy(busy, true)),
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (msg) => dispatch(notifyInfo(msg)),
};
};
AddEditHost.propTypes = {
Close: PropTypes.func.isRequired,
HostData: PropTypes.object,
HostList: PropTypes.array.isRequired,
InstalledVersion: PropTypes.string.isRequired,
completeAddEditHost: PropTypes.func.isRequired,
notifyApplicationBusy: PropTypes.func.isRequired,
notifyError: PropTypes.func.isRequired,
notifyInfo: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(AddEditHost);

View File

@@ -1,31 +1,17 @@
import React from 'react'; import React from 'react';
import {Component} from 'react'; import PropTypes from 'prop-types';
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 => {
return {
RemoteMounts: state.mounts.RemoteMounts,
S3Mounts: state.mounts.S3Mounts,
};
};
const mapDispatchToProps = dispatch => {
return {
addRemoteMount: (hostNameOrIp, port, token) => dispatch(addRemoteMount(hostNameOrIp, port, token)),
addS3Mount: (name, accessKey, secretKey, region, bucketName, url) => dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName, url)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
}
};
const default_state = { const default_state = {
AccessKey: '', AccessKey: '',
BucketName: '', BucketName: '',
@@ -35,12 +21,14 @@ const default_state = {
Name: '', Name: '',
Port: 20000, Port: 20000,
Provider: Constants.S3_PROVIDER_LIST[0], Provider: Constants.S3_PROVIDER_LIST[0],
Region: Constants.S3_REGION_PROVIDER_REGION[0], Region: Constants.S3_REGION_PROVIDER_REGION[0][0],
SecretKey: '', SecretKey: '',
Token: '', Token: '',
CustomURL: '',
CustomRegion: '',
}; };
export default connect(mapStateToProps, mapDispatchToProps)(class extends Component { class AddMount extends Component {
state = { state = {
...default_state, ...default_state,
}; };
@@ -53,39 +41,59 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
if (this.props.RemoteMounts.includes(provider)) { if (this.props.RemoteMounts.includes(provider)) {
this.props.notifyError('Remote host already exists'); this.props.notifyError('Remote host already exists');
} else { } else {
this.setState({ this.setState(
DisplayRemote: false {
}, () => { DisplayRemote: false,
this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token); },
this.setState({ () => {
...default_state, this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token);
}); this.setState({
}); ...default_state,
});
}
);
} }
} }
}; };
addS3Mount = () => { addS3Mount = () => {
const isCustomProvider = this.state.Provider === Constants.S3_CUSTOM_PROVIDER;
const isCustomRegion = this.state.Region === Constants.S3_CUSTOM_REGION;
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 if (isCustomProvider && !this.state.CustomURL.trim()) {
this.props.notifyError('Custom URL cannot be empty.');
} else if (isCustomRegion && !this.state.CustomRegion.trim()) {
this.props.notifyError('Custom Region cannot be empty.');
} else { } else {
const provider = 'S3' + this.state.Name; const provider = 'S3' + this.state.Name;
if (this.props.S3Mounts.includes(provider)) { if (this.props.S3Mounts.includes(provider)) {
this.props.notifyError('Remote host already exists'); this.props.notifyError('Remote host already exists');
} else { } else {
this.setState({ this.setState(
DisplayS3: false {
}, () => { DisplayS3: false,
this.props.addS3Mount(this.state.Name, this.state.AccessKey, this.state.SecretKey, },
this.state.Region, this.state.BucketName, Constants.S3_PROVIDER_URL[this.state.Provider]); () => {
this.setState({ this.props.addS3Mount(
...default_state, this.state.Name,
}); this.state.AccessKey,
}); this.state.SecretKey,
isCustomRegion ? this.state.CustomRegion : this.state.Region,
this.state.BucketName,
isCustomProvider
? this.state.CustomURL
: Constants.S3_PROVIDER_URL[this.state.Provider]
);
this.setState({
...default_state,
});
}
);
} }
} }
}; };
@@ -105,142 +113,257 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
}; };
render() { render() {
const displayAddRemote = createModalConditionally(this.state.DisplayRemote, ( const displayAddRemote = createModalConditionally(
<Box dxDark this.state.DisplayRemote,
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}> <Box
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add Remote dxDark
Mount</h1> dxStyle={{
<Text text={'Hostname or IP'} width: 'auto',
textAlign={'left'} height: 'auto',
type={'Heading2'}/> padding: 'var(--default_spacing)',
<input onChange={e => this.setState({HostNameOrIp: e.target.value.trim()})} }}>
className={'ConfigurationItemInput'} <h1
type={'text'} style={{
value={this.state.HostNameOrIp}/> textAlign: 'center',
<div style={{paddingTop: 'var(--default_spacing)'}}/> paddingBottom: 'var(--default_spacing)',
<Text text={'Port'} }}>
textAlign={'left'} Add Remote Mount
type={'Heading2'}/> </h1>
<input max={65535} <Text text={'Hostname or IP'} textAlign={'left'} type={'Heading2'} />
min={1025} <input
onChange={e => this.setState({Port: e.target.value})} onChange={(e) => this.setState({ HostNameOrIp: e.target.value.trim() })}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'text'}
value={this.state.Port}/> value={this.state.HostNameOrIp}
<div style={{paddingTop: 'var(--default_spacing)'}}/> />
<Text text={'Remote Token'} <div style={{ paddingTop: 'var(--default_spacing)' }} />
textAlign={'left'} <Text text={'Port'} textAlign={'left'} type={'Heading2'} />
type={'Heading2'}/> <input
<input onChange={e => this.setState({Token: e.target.value})} max={65535}
className={'ConfigurationItemInput'} min={1025}
type={'text'} onChange={(e) => this.setState({ Port: e.target.value })}
value={this.state.Token}/> className={'ConfigurationItemInput'}
<div style={{paddingTop: 'var(--default_spacing)'}}/> type={'number'}
<div style={{display: 'flex', flexDirection: 'row'}}> value={this.state.Port}
<Button buttonStyles={{width: '100%'}} />
clicked={() => this.addRemoteMount()}>OK</Button> <div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{paddingLeft: 'var(--default_spacing)'}}/> <Text text={'Remote Token'} textAlign={'left'} type={'Heading2'} />
<Button buttonStyles={{width: '100%'}} <input
clicked={() => this.setState({DisplayRemote: false})}>Cancel</Button> 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> </div>
</Box> </Box>
)); );
const displayAddS3 = createModalConditionally(this.state.DisplayS3, ( const displayAddS3 = createModalConditionally(
<Box dxDark this.state.DisplayS3,
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}> <Box
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add S3 dxDark
Mount</h1> dxStyle={{
<div style={{display: 'flex', flexDirection: 'row'}}> width: 'auto',
<Text text={'Name'} height: 'auto',
textAlign={'left'} padding: 'var(--default_spacing)',
type={'Heading2'}/> }}>
<div style={{paddingLeft: 'var(--default_spacing)'}}/> <h1
<Text text={'Provider'} style={{
textAlign={'left'} textAlign: 'center',
type={'Heading2'}/> 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>
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{ display: 'flex', flexDirection: 'row' }}>
<input onChange={e => this.setState({Name: e.target.value.trim()})} <input
className={'ConfigurationItemInput'} onChange={(e) => this.setState({ Name: e.target.value.trim() })}
style={{width: '100%'}} className={'ConfigurationItemInput'}
type={'text'} style={{ width: '100%' }}
value={this.state.Name}/> type={'text'}
<div style={{paddingLeft: 'var(--default_spacing)'}}/> value={this.state.Name}
<DropDown changed={e => this.setState({Provider: e.target.value})} />
items={Constants.S3_PROVIDER_LIST} <div style={{ paddingLeft: 'var(--default_spacing)' }} />
selected={this.state.Provider}/> <DropDown
changed={(e) =>
this.setState({
Provider: e.target.value,
Region:
Constants.S3_REGION_PROVIDER_REGION[
Constants.S3_PROVIDER_LIST.indexOf(e.target.value)
][0],
})
}
items={Constants.S3_PROVIDER_LIST}
selected={this.state.Provider}
/>
</div> </div>
<div style={{paddingTop: 'var(--default_spacing)'}}/> <div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{display: 'flex', flexDirection: 'row'}}> {this.state.Provider === Constants.S3_CUSTOM_PROVIDER ? (
<Text text={'Bucket Name (optional)'} <div>
textAlign={'left'} <div style={{ display: 'flex', flexDirection: 'row' }}>
type={'Heading2'}/> <Text text={'Custom URL'} textAlign={'left'} type={'Heading2'} />
<div style={{paddingLeft: 'var(--default_spacing)'}}/> </div>
<Text text={'Region'} <div style={{ display: 'flex', flexDirection: 'row' }}>
textAlign={'left'} <input
type={'Heading2'}/> onChange={(e) => this.setState({ CustomURL: e.target.value })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.CustomURL}
/>
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
</div>
) : null}
<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>
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{ display: 'flex', flexDirection: 'row' }}>
<input onChange={e => this.setState({BucketName: e.target.value})} <input
className={'ConfigurationItemInput'} onChange={(e) => this.setState({ BucketName: e.target.value })}
style={{width: '100%'}} className={'ConfigurationItemInput'}
type={'text'} style={{ width: '100%' }}
value={this.state.BucketName}/> type={'text'}
<div style={{paddingLeft: 'var(--default_spacing)'}}/> value={this.state.BucketName}
<input onChange={e => this.setState({Region: e.target.value})} />
className={'ConfigurationItemInput'} <div style={{ paddingLeft: 'var(--default_spacing)' }} />
type={'text'} <DropDown
value={this.state.Region}/> changed={(e) =>
this.setState({
Region: e.target.value,
})
}
items={
Constants.S3_REGION_PROVIDER_REGION[
Constants.S3_PROVIDER_LIST.indexOf(this.state.Provider)
]
}
selected={this.state.Region}
/>
</div> </div>
<div style={{paddingTop: 'var(--default_spacing)'}}/> <div style={{ paddingTop: 'var(--default_spacing)' }} />
<div style={{display: 'flex', flexDirection: 'row'}}> {this.state.Region === Constants.S3_CUSTOM_REGION ? (
<Text text={'Access Key'} <div>
textAlign={'left'} <div style={{ display: 'flex', flexDirection: 'row' }}>
type={'Heading2'}/> <div style={{ paddingLeft: 'var(--default_spacing)', width: '100%' }} />
<div style={{paddingLeft: 'var(--default_spacing)'}}/> <Text text={'Custom Region'} textAlign={'left'} type={'Heading2'} />
<Text text={'Secret Key'} </div>
textAlign={'left'} <div style={{ display: 'flex', flexDirection: 'row' }}>
type={'Heading2'}/> <div style={{ paddingLeft: 'var(--default_spacing)', width: '100%' }} />
<input
onChange={(e) => this.setState({ CustomRegion: e.target.value })}
className={'ConfigurationItemInput'}
style={{ width: '100%' }}
type={'text'}
value={this.state.CustomRegion}
/>
</div>
<div style={{ paddingTop: 'var(--default_spacing)' }} />
</div>
) : null}
<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>
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{ display: 'flex', flexDirection: 'row' }}>
<input onChange={e => this.setState({AccessKey: e.target.value})} <input
className={'ConfigurationItemInput'} onChange={(e) => this.setState({ AccessKey: e.target.value })}
type={'text'} className={'ConfigurationItemInput'}
value={this.state.AccessKey}/> type={'text'}
<div style={{paddingLeft: 'var(--default_spacing)'}}/> value={this.state.AccessKey}
<input onChange={e => this.setState({SecretKey: e.target.value})} />
className={'ConfigurationItemInput'} <div style={{ paddingLeft: 'var(--default_spacing)' }} />
type={'text'} <input
value={this.state.SecretKey}/> onChange={(e) => this.setState({ SecretKey: e.target.value })}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.SecretKey}
/>
</div> </div>
<div style={{paddingTop: 'calc(var(--default_spacing) * 2)'}}/> <div style={{ paddingTop: 'calc(var(--default_spacing) * 2)' }} />
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{ display: 'flex', flexDirection: 'row' }}>
<div style={{width: '200%'}}/> <div style={{ width: '200%' }} />
<Button buttonStyles={{width: '100%'}} <Button buttonStyles={{ width: '100%' }} clicked={() => this.addS3Mount()}>
clicked={() => this.addS3Mount()}>OK</Button> OK
<div style={{paddingLeft: 'var(--default_spacing)'}}/> </Button>
<Button buttonStyles={{width: '100%'}} <div style={{ paddingLeft: 'var(--default_spacing)' }} />
clicked={() => this.setState({DisplayS3: false})}>Cancel</Button> <Button
buttonStyles={{ width: '100%' }}
clicked={() => this.setState({ DisplayS3: false })}>
Cancel
</Button>
</div> </div>
</Box> </Box>
)); );
return ( return (
<div className={'AddMount'}> <div className={'AddMount'}>
{displayAddRemote} {displayAddRemote}
{displayAddS3} {displayAddS3}
<div className={'AddMountButtons'}> <div className={'AddMountButtons'}>
{this.props.remoteSupported ? {this.props.remoteSupported ? (
<Button className={'AddMountButton'} <Button className={'AddMountButton'} clicked={this.handleAddRemoteMount}>
clicked={this.handleAddRemoteMount}>Add Remote Mount</Button> : null} Add Remote Mount
{this.props.remoteSupported && this.props.s3Supported ? </Button>
<div style={{paddingRight: 'var(--default_spacing)'}}/> : null} ) : null}
{this.props.s3Supported ? {this.props.remoteSupported && this.props.s3Supported ? (
<Button className={'AddMountButton'} <div style={{ paddingRight: 'var(--default_spacing)' }} />
clicked={this.handleAddS3Mount}>Add S3 Mount</Button> : null} ) : null}
{this.props.s3Supported ? (
<Button className={'AddMountButton'} clicked={this.handleAddS3Mount}>
Add S3 Mount
</Button>
) : null}
</div> </div>
</div> </div>
); );
} }
}); }
const mapStateToProps = (state) => {
return {
RemoteMounts: state.mounts.RemoteMounts,
S3Mounts: state.mounts.S3Mounts,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addRemoteMount: (hostNameOrIp, port, token) =>
dispatch(addRemoteMount(hostNameOrIp, port, token)),
addS3Mount: (name, accessKey, secretKey, region, bucketName, url) =>
dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName, url)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
};
};
AddMount.propTypes = {
RemoteMounts: PropTypes.array.isRequired,
S3Mounts: PropTypes.array.isRequired,
addRemoteMount: PropTypes.func.isRequired,
addS3Mount: PropTypes.func.isRequired,
notifyError: PropTypes.func.isRequired,
remoteSupported: PropTypes.bool,
s3Supported: PropTypes.bool,
};
export default connect(mapStateToProps, mapDispatchToProps)(AddMount);

View File

@@ -1,14 +1,16 @@
import React from 'react'; import React from 'react';
import './Configuration.css'; import './Configuration.css';
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 IPCContainer from '../IPCContainer/IPCContainer'; import IPCContainer from '../IPCContainer/IPCContainer';
import {displayConfiguration} from '../../redux/actions/mount_actions'; import Modal from '../../components/UI/Modal/Modal';
import {notifyError} from '../../redux/actions/error_actions'; import PropTypes from 'prop-types';
import {displayPinnedManager} from '../../redux/actions/pinned_manager_actions'; import { connect } from 'react-redux';
import { createDismissDisplay } from '../../utils.jsx';
import { displayConfiguration } from '../../redux/actions/mount_actions';
import { displayPinnedManager } from '../../redux/actions/pinned_manager_actions';
import { notifyError } from '../../redux/actions/error_actions';
const Constants = require('../../constants'); const Constants = require('../../constants');
@@ -25,7 +27,7 @@ class Configuration extends IPCContainer {
ItemList: [], ItemList: [],
Saving: false, Saving: false,
ShowAdvanced: false, ShowAdvanced: false,
Template: {} Template: {},
}; };
checkItemChanged = (itemA, itemB) => { checkItemChanged = (itemA, itemB) => {
@@ -33,29 +35,44 @@ 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;
}
if (itemA.type === 'host_list') {
if (itemA.value.length !== itemB.value.length) {
return true;
}
return (
itemA.value.filter((i) =>
itemB.value.find(
(j) =>
j.HostNameOrIp === i.HostNameOrIp &&
j.ApiPort === i.ApiPort &&
j.Protocol === i.Protocol &&
j.TimeoutMs === i.TimeoutMs &&
j.AgentString === i.AgentString &&
j.ApiPassword === i.ApiPassword &&
j.AuthURL === i.AuthURL &&
j.AuthUser === i.AuthUser &&
j.AuthPassword === i.AuthPassword
)
).length != itemA.value.length
);
} }
return itemA.value !== itemB.value; return itemA.value !== itemB.value;
}; };
checkSaveRequired = () => { checkSaveRequired = () => {
const changedItems = [];
let i = 0; let i = 0;
for (const item of this.state.ItemList) { const changedItems = this.state.ItemList.filter((item) => {
if (this.checkItemChanged(this.state.OriginalItemList[i++], item)) { return this.checkItemChanged(this.state.OriginalItemList[i++], item);
changedItems.push(item); });
}
}
let changedObjectLookup = null; let changedObjectLookup = null;
for (const key of Object.keys(this.state.ObjectLookup)) { for (const key of Object.keys(this.state.ObjectLookup)) {
const changedObjectItems = [];
let j = 0; let j = 0;
for (const item of this.state.ObjectLookup[key]) { const changedObjectItems = this.state.ObjectLookup[key].filter((item) => {
if (this.checkItemChanged(this.state.OriginalObjectLookup[key][j++], item)) { return this.checkItemChanged(this.state.OriginalObjectLookup[key][j++], item);
changedObjectItems.push(item); });
}
}
if (changedObjectItems.length > 0) { if (changedObjectItems.length > 0) {
if (changedObjectLookup === null) { if (changedObjectLookup === null) {
@@ -65,7 +82,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,
@@ -95,62 +112,69 @@ 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] &&
config[key] : (template[key].type === 'string_array' || template[key].type === 'object')
(template[key] && (template[key].type === 'string_array')) ? ? config[key]
config[key] : : template[key] && template[key].type === 'host_list'
config[key].toString(), ? config[key]
}; : config[key].toString(),
}) };
.filter(i => { })
let ret = template[i.label]; .filter((i) => {
if (ret && (template[i.label].type === 'object')) { let ret = template[i.label];
objectList.push(i); if (ret && template[i.label].type === 'object') {
ret = false; objectList.push(i);
} 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.type === 'host_list'
? target.value
: 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.type === 'host_list'
? target.value
: target.value.toString();
objectLookup[name] = itemList; objectLookup[name] = itemList;
this.setState({ this.setState({
ObjectLookup: objectLookup, ObjectLookup: objectLookup,
}); });
}; };
onGetConfigReply = (event, arg) => { onGetConfigReply = (_, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
const list = this.createItemList(arg.data.Config, this.state.Template); const list = this.createItemList(arg.data.Config, this.state.Template);
const itemListCopy = JSON.parse(JSON.stringify(list.ItemList)); const itemListCopy = JSON.parse(JSON.stringify(list.ItemList));
@@ -161,8 +185,9 @@ class Configuration extends IPCContainer {
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,32 +197,36 @@ 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);
} }
}; };
onGetConfigTemplateReply = (event, arg) => { onGetConfigTemplateReply = (_, 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 +241,97 @@ 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({ let changedItems = this.state.ChangedItems.map((item) => {
Name: item.label, return {
Value: item.type === 'string_array' ? Name: item.label,
item.value.join(';') : Value:
item.value, item.type === 'string_array'
? item.value.join(';')
: item.type === 'host_list'
? JSON.stringify(item.value)
: 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]) { changedItems = changedItems.concat(
changedItems.push({ this.state.ChangedObjectLookup[key].map((item) => {
Name: key + '.' + item.label, return {
Value: item.type === 'string_array' ? Name: key + '.' + item.label,
item.value.join(';') : Value:
item.value, item.type === 'string_array'
}); ? item.value.join(';')
: item.type === 'host_list'
? JSON.stringify(item.value)
: 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(itemList.find((s) => s.label === 'IsRemoteMount').value);
JSON.parse(itemList.find(s => s.label === 'EnableRemoteMount').value); const enableRemoteMount =
return (item.label === 'RemoteHostNameOrIp') || (item.label === 'RemoteMaxConnections') ? !isRemoteMount && JSON.parse(itemList.find((s) => s.label === 'EnableRemoteMount').value);
isRemoteMount : return item.label === 'RemoteHostNameOrIp' || item.label === 'RemoteMaxConnections'
(item.label === 'RemoteReceiveTimeoutSeconds') || (item.label === 'RemoteSendTimeoutSeconds') || (item.label === 'RemotePort') || (item.label === 'RemoteToken') ? ? isRemoteMount
isRemoteMount || enableRemoteMount : : item.label === 'RemoteReceiveTimeoutSeconds' ||
(item.label === 'EnableRemoteMount') ? item.label === 'RemoteSendTimeoutSeconds' ||
!isRemoteMount : item.label === 'RemotePort' ||
enableRemoteMount; 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' }}>Save Changes?</h1>
<table width='100%'> <table width="100%">
<tbody> <tbody>
<tr> <tr>
<td align='center' width='50%'><Button clicked={this.saveAndClose} <td align="center" width="50%">
disabled={this.state.Saving}>Yes</Button> <Button clicked={this.saveAndClose} disabled={this.state.Saving}>
</td> Yes
<td align='center' width='50%'><Button clicked={this.props.hideConfiguration} </Button>
disabled={this.state.Saving}>No</Button></td> </td>
</tr> <td align="center" width="50%">
<Button clicked={this.props.hideConfiguration} disabled={this.state.Saving}>
No
</Button>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</Box> </Box>
@@ -291,87 +340,98 @@ class Configuration extends IPCContainer {
} }
let autoFocus = true; let autoFocus = true;
const getAutoFocus = () => {
return autoFocus;
};
const setAutoFocus = (value) => {
autoFocus = value;
};
let objectItems = []; const objectItems = Object.keys(this.state.ObjectLookup).map((key) => {
for (const key of Object.keys(this.state.ObjectLookup)) { return (
objectItems.push((
<div key={key}> <div key={key}>
<h2>{key}</h2> <h2>{key}</h2>
<div> <div>
{ {this.state.ObjectLookup[key]
this.state.ObjectLookup[key].map((k, i) => { .map((k, i) => {
const shouldFocus = autoFocus; const shouldFocus = getAutoFocus();
autoFocus = false; setAutoFocus(false);
return ( return !k.advanced ||
(!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ? (this.state.ShowAdvanced && k.advanced && !k.remote) ||
<ConfigurationItem advanced={k.advanced} this.showRemoteConfigItem(k, this.state.ObjectLookup[key]) ? (
autoFocus={shouldFocus} <ConfigurationItem
changed={e => this.handleObjectItemChanged(e, key, i)} advanced={k.advanced}
grouping={key} autoFocus={shouldFocus}
items={this.state.Template[key].template[k.label].items} changed={(e) => this.handleObjectItemChanged(e, key, i)}
key={i} grouping={key}
label={k.label} items={this.state.Template[key].template[k.label].items}
readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))} key={i}
template={this.state.Template[key].template[k.label]} label={k.label}
value={k.value}/> : readOnly={
null) this.state.IsRemoteMount &&
(k.label === 'RemoteHostNameOrIp' || k.label === 'RemotePort')
}
template={this.state.Template[key].template[k.label]}
value={k.value}
/>
) : null;
}) })
} .filter((i) => i !== null)}
</div> </div>
</div> </div>
));
}
const configurationItems = this.state.ItemList
.map((k, i) => {
const shouldFocus = autoFocus;
autoFocus = false;
return (
((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
<ConfigurationItem advanced={k.advanced}
autoFocus={shouldFocus}
changed={e => this.handleItemChanged(e, i)}
grouping={'Settings'}
items={this.state.Template[k.label].items}
key={i}
label={k.label}
template={this.state.Template[k.label]}
value={k.value}/> :
null
); );
}); });
const configurationItems = this.state.ItemList.map((k, i) => {
const shouldFocus = autoFocus;
autoFocus = false;
return (!this.state.IsRemoteMount || !k.hide_remote) &&
(!k.advanced || (this.state.ShowAdvanced && k.advanced)) ? (
<ConfigurationItem
advanced={k.advanced}
autoFocus={shouldFocus}
changed={(e) => this.handleItemChanged(e, i)}
grouping={'Settings'}
items={this.state.Template[k.label].items}
key={i}
label={k.label}
template={this.state.Template[k.label]}
value={k.value}
/>
) : null;
}).filter((i) => i !== null);
return ( return (
<div className={'Configuration'}> <div className={'Configuration'}>
{confirmSave} {confirmSave}
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}> <Box dxDark dxStyle={{ padding: '5px' }}>
<div style={{ {createDismissDisplay(this.checkSaveRequired)}
float: 'right', <h1 style={{ width: '100%', textAlign: 'center' }}>
margin: 0, {(this.props.DisplayRemoteConfiguration
padding: 0, ? this.props.DisplayConfiguration.substr(6)
marginTop: '-4px', : this.props.DisplayS3Configuration
boxSizing: 'border-box', ? this.props.DisplayConfiguration.substr(2)
display: 'block' : this.props.DisplayConfiguration) + ' Configuration '}
}}>
<a href={'#'}
onClick={this.checkSaveRequired}
style={{cursor: 'pointer'}}>X</a>
</div>
<h1 style={{width: '100%', textAlign: 'center'}}>{(
this.props.DisplayRemoteConfiguration ?
this.props.DisplayConfiguration.substr(6) :
this.props.DisplayConfiguration) + ' Configuration '}
</h1> </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 +440,33 @@ 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)),
} };
};
Configuration.propTypes = {
displayPinnedManager: PropTypes.func.isRequired,
DisplayConfiguration: PropTypes.string.isRequired,
hideConfiguration: PropTypes.func.isRequired,
remoteSupported: PropTypes.bool.isRequired,
notifyError: PropTypes.func.isRequired,
MState: PropTypes.object.isRequired,
Platform: PropTypes.string.isRequired,
version: PropTypes.string.isRequired,
}; };
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,23 @@
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 {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {
notifyError,
notifyInfo
} from '../../../redux/actions/error_actions';
import settings from '../../../assets/settings';
import DropDown from '../../../components/UI/DropDown/DropDown'; import DropDown from '../../../components/UI/DropDown/DropDown';
import HostList from '../../HostList/HostList';
import Password from '../../../containers/UI/Password/Password'; import Password from '../../../containers/UI/Password/Password';
import PropTypes from 'prop-types';
import settings from '../../../assets/settings';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { connect } from 'react-redux';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { notifyError, notifyInfo } from '../../../redux/actions/error_actions';
const mapDispatchToProps = dispatch => { const ConfigurationItem = (props) => {
return {
notifyError: msg => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
}
};
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 +29,204 @@ 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 'host_list':
data = (
<HostList
autoFocus={props.autoFocus}
disabled={props.readOnly}
onChange={(items) =>
handleChanged({
target: {
type: 'host_list',
value: items,
},
})
}
type={props.template.subtype}
value={props.value}
/>
);
break;
case 'list': 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,17 +234,44 @@ 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>
</table> </table>
</div> </div>
); );
}); };
const mapDispatchToProps = (dispatch) => {
return {
notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
};
};
ConfigurationItem.propTypes = {
autoFocus: PropTypes.bool,
changed: PropTypes.func.isRequired,
grouping: PropTypes.string,
items: PropTypes.array,
label: PropTypes.string,
notifyError: PropTypes.func.isRequired,
notifyInfo: PropTypes.func.isRequired,
readOnly: PropTypes.bool,
template: PropTypes.object.isRequired,
value: PropTypes.oneOfType([PropTypes.array, PropTypes.string, PropTypes.number]),
};
export default connect(null, mapDispatchToProps)(ConfigurationItem);

View File

View File

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

View File

View File

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

View File

@@ -1,22 +1,22 @@
import {Component} from 'react'; import { Component } from 'react';
import {getIPCRenderer} from '../../utils'; import { getIPCRenderer } from '../../utils.jsx';
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
export default class IPCContainer extends Component { class IPCContainer extends Component {
handlerList = {}; handlerList = {};
componentWillUnmount() { componentWillUnmount() {
if (ipcRenderer) { if (ipcRenderer) {
for (let name in this.handlerList) { for (let name in this.handlerList) {
if (this.handlerList.hasOwnProperty(name)) { if (Object.prototype.hasOwnProperty.call(this.handlerList, name)) {
ipcRenderer.removeListener(name, this.handlerList[name]); ipcRenderer.removeListener(name, this.handlerList[name]);
} }
} }
} }
this.handlerList = {}; this.handlerList = {};
}; }
sendRequest = (name, data) => { sendRequest = (name, data) => {
if (ipcRenderer) { if (ipcRenderer) {
@@ -41,5 +41,6 @@ export default class IPCContainer extends Component {
ipcRenderer.on(name, callback); ipcRenderer.on(name, callback);
} }
}; };
}
}; export default IPCContainer;

View File

@@ -1,46 +1,26 @@
import React from 'react'; import React from 'react';
import './MountItem.css'; import './MountItem.css';
import {connect} from 'react-redux';
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 CheckBox from '../../../components/UI/CheckBox/CheckBox';
import Text from '../../../components/UI/Text/Text'; import DropDown from '../../../components/UI/DropDown/DropDown';
import Grid from '../../../components/UI/Grid/Grid'; import Grid from '../../../components/UI/Grid/Grid';
import configureImage from '../../../assets/images/configure.png'; import Loader from 'react-loader-spinner';
import PropTypes from 'prop-types';
import RootElem from '../../../components/UI/RootElem/RootElem'; import RootElem from '../../../components/UI/RootElem/RootElem';
import Text from '../../../components/UI/Text/Text';
import configureImage from '../../../assets/images/configure.png';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { connect } from 'react-redux';
import { import {
displayConfiguration, displayConfiguration,
removeMount, removeMount,
setProviderState setProviderState,
} from '../../../redux/actions/mount_actions'; } from '../../../redux/actions/mount_actions';
import { import { displaySkynetExport, displaySkynetImport } from '../../../redux/actions/skynet_actions';
displaySkynetExport, import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
displaySkynetImport,
} from '../../../redux/actions/skynet_actions'
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import { faTrashAlt} from '@fortawesome/free-solid-svg-icons';
import CheckBox from '../../../components/UI/CheckBox/CheckBox';
const mapStateToProps = (state, ownProps) => { const MountItem = (props) => {
return { const handleAutoMountChanged = (e) => {
MState: state.mounts.MountState[ownProps.provider],
Platform: state.common.Platform,
PState: state.mounts.ProviderState[ownProps.provider]
};
};
const mapDispatchToProps = dispatch => {
return {
displayConfiguration: (provider, remote, s3) => dispatch(displayConfiguration(provider, remote, s3)),
displaySkynetExport: display => dispatch(displaySkynetExport(display)),
displaySkynetImport: display => dispatch(displaySkynetImport(display)),
removeMount: provider => dispatch(removeMount(provider)),
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(props => {
const handleAutoMountChanged = e => {
const state = { const state = {
...props.PState, ...props.PState,
AutoMount: e.target.checked, AutoMount: e.target.checked,
@@ -48,7 +28,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 +37,23 @@ 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
}} ? () => props.displayConfiguration(props.provider, props.remote, props.s3)
src={configureImage} : (e) => {
style={{padding: 0, border: 0, margin: 0, ...pointer}} e.preventDefault();
width={'16px'}/> }
}
src={configureImage}
style={{ padding: 0, border: 0, margin: 0, ...pointer }}
width={'16px'}
/>
</RootElem> </RootElem>
); );
@@ -77,80 +62,95 @@ 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 colSpan={inputColumnSpan - 8} key={'i' + key++} row={secondRow} rowSpan={7}>
key={'i' + key++} <input
row={secondRow} disabled={!props.MState.AllowMount || props.MState.Mounted}
rowSpan={7}> maxLength={4096}
<input disabled={!props.MState.AllowMount || props.MState.Mounted} onChange={props.changed}
maxLength={4096} size={4096}
onChange={props.changed} className={'MountItemInput'}
size={4096} type={'text'}
className={'MountItemInput'} value={props.PState.MountLocation}
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={() => props.browseClicked(props.provider, props.PState.MountLocation)}
colSpan={7} col={inputColumnSpan - 7}
disabled={props.MState.Mounted || !props.MState.AllowMount} colSpan={7}
key={'b' + key++} disabled={props.MState.Mounted || !props.MState.AllowMount}
row={secondRow} key={'b' + key++}
rowSpan={7}>...</Button> 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 col={inputColumnSpan + 24} colSpan={28} row={secondRow} rowSpan={7}>
colSpan={28} <CheckBox
row={secondRow} changed={handleAutoMountChanged}
rowSpan={7}> checked={props.PState.AutoMount}
<CheckBox changed={handleAutoMountChanged} label={'Auto-mount'}
checked={props.PState.AutoMount} />
label={'Auto-mount'}/>
</RootElem> </RootElem>
); );
const autoRestartControl = ( const autoRestartControl = (
<RootElem col={inputColumnSpan + 24 + 28} <RootElem col={inputColumnSpan + 24 + 28} colSpan={24} row={secondRow} rowSpan={7}>
colSpan={24} <CheckBox
row={secondRow} changed={handleAutoRestartChanged}
rowSpan={7}> checked={props.PState.AutoRestart}
<CheckBox changed={handleAutoRestartChanged} label={'Restart'}
checked={props.PState.AutoRestart} />
label={'Restart'}/>
</RootElem> </RootElem>
); );
@@ -158,7 +158,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 +166,15 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
} }
}; };
removeControl = ( removeControl = (
<RootElem col={dimensions=>dimensions.columns - 6} <RootElem col={(dimensions) => dimensions.columns - 6} row={secondRow + 3}>
row={secondRow + 3}> <a href={'#'} onClick={handleRemoveMount} style={removeStyle}>
<a href={'#'} <FontAwesomeIcon icon={faTrashAlt} />
onClick={handleRemoveMount}
style={removeStyle}>
<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 +183,47 @@ 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}
@@ -218,4 +235,43 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
</Grid> </Grid>
</div> </div>
); );
}); };
const mapStateToProps = (state, ownProps) => {
return {
MState: state.mounts.MountState[ownProps.provider],
Platform: state.common.Platform,
PState: state.mounts.ProviderState[ownProps.provider],
};
};
const mapDispatchToProps = (dispatch) => {
return {
displayConfiguration: (provider, remote, s3) =>
dispatch(displayConfiguration(provider, remote, s3)),
displaySkynetExport: (display) => dispatch(displaySkynetExport(display)),
displaySkynetImport: (display) => dispatch(displaySkynetImport(display)),
removeMount: (provider) => dispatch(removeMount(provider)),
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
};
};
MountItem.propTypes = {
MState: PropTypes.object.isRequired,
PState: PropTypes.object.isRequired,
Platform: PropTypes.string.isRequired,
allowRemove: PropTypes.bool,
browseClicked: PropTypes.func.isRequired,
changed: PropTypes.func.isRequired,
clicked: PropTypes.func.isRequired,
displayConfiguration: PropTypes.func.isRequired,
displaySkynetExport: PropTypes.func.isRequired,
displaySkynetImport: PropTypes.func.isRequired,
provider: PropTypes.string.isRequired,
remote: PropTypes.bool,
removeMount: PropTypes.func.isRequired,
s3: PropTypes.bool,
setProviderState: PropTypes.func.isRequired,
};
export default connect(mapStateToProps, mapDispatchToProps)(MountItem);

View File

@@ -1,7 +1,7 @@
.MountItems { .MountItems {
padding: 0; padding: 0;
margin: 0; margin: 0;
height: 121px; height: 212px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; overflow: hidden;
@@ -10,7 +10,7 @@
.MountItemsRemote { .MountItemsRemote {
padding: 0; padding: 0;
margin: 0; margin: 0;
height: 121px; height: 212px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;

View File

@@ -1,12 +1,13 @@
import React from 'react'; import React from 'react';
import './MountItems.css';
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 IPCContainer from '../IPCContainer/IPCContainer';
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';
import IPCContainer from '../IPCContainer/IPCContainer'; import { connect } from 'react-redux';
import { notifyError } from '../../redux/actions/error_actions';
import { import {
resetMountsState, resetMountsState,
setAllowMount, setAllowMount,
@@ -14,9 +15,8 @@ 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';
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,15 +43,18 @@ 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(); );
});
} }
}; };
@@ -64,18 +67,16 @@ class MountItems extends IPCContainer {
} }
componentWillUnmount() { componentWillUnmount() {
for (const provider in this.state.RetryItems) { Object.keys(this.state.RetryItems).forEach((provider) => {
if (this.state.RetryItems.hasOwnProperty(provider)) { this.cancelRetryMount(provider);
this.cancelRetryMount(provider); });
}
}
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 +87,9 @@ class MountItems extends IPCContainer {
}); });
}; };
detectMounts = ()=> { detectMounts = () => {
if (!this.state.DisplayRetry) { if (!this.state.DisplayRetry) {
const providerList = this.getProviderList(); this.getProviderList().forEach((provider) => {
providerList.forEach(provider => {
this.detectMount(provider); this.detectMount(provider);
}); });
} }
@@ -98,7 +98,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 +110,31 @@ 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(() => {
}; const 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(provider, remote, s3, true, mountLocation);
retryItems[provider].RetrySeconds = retrySeconds; });
this.setState({ } else {
RetryItems: retryItems, retryItems[provider].RetrySeconds = retrySeconds;
}); this.setState({
} RetryItems: retryItems,
}, 1000); });
}); }
}, 1000);
}
);
} }
}; };
@@ -140,7 +143,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,19 +157,23 @@ 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;
const displayRetry = (msg) => {
this.displayRetryMount(provider, remote, s3, location, msg);
};
if (result.Success) { if (result.Success) {
if (result.Valid) { if (result.Valid) {
if (this.props.Platform !== 'win32') { if (this.props.Platform !== 'win32') {
@@ -180,20 +187,30 @@ 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 + '.'); ) {
displayRetry('Failed to connect to ' + provider + ' daemon');
} else if (
result.Code === new Uint32Array([-3])[0] ||
result.Code === new Uint8Array([-3])[0]
) {
displayRetry(
'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.'
);
} else { } else {
this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error); displayRetry('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); displayRetry('Version check failed: ' + result.Error);
} }
} }
} }
@@ -202,32 +219,24 @@ class MountItems extends IPCContainer {
this.addMountsBusy(provider); this.addMountsBusy(provider);
this.props.setAllowMount(provider, false); this.props.setAllowMount(provider, false);
if (mount) { this.sendRequest(mount ? Constants.IPC_Mount_Drive : Constants.IPC_Unmount_Drive, {
this.sendRequest(Constants.IPC_Mount_Drive, { Location: location,
Location: location, Provider: provider,
Provider: provider, Remote: remote,
Remote: remote, S3: s3,
S3: s3, Version: this.props.InstalledVersion,
Version: this.props.InstalledVersion, });
});
} else {
this.sendRequest(Constants.IPC_Unmount_Drive, {
Location: location,
Provider: provider,
Remote: remote,
S3: s3,
Version: this.props.InstalledVersion,
});
}
} }
} }
}; };
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,26 +249,22 @@ 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)) { return !!Object.keys(this.props.MountState).find((provider) => {
if (this.props.MountState[provider].Mounted) return this.props.MountState[provider].Mounted;
return true; });
}
return false;
}; };
onDetectMountReply = (event, arg) => { onDetectMountReply = (_, 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 +272,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 {
@@ -277,14 +287,20 @@ class MountItems extends IPCContainer {
} }
}; };
onMountDriveReply = (event, arg) => { onMountDriveReply = (_, arg) => {
this.props.setMounted(arg.data.Provider, arg.data.Success); this.props.setMounted(arg.data.Provider, arg.data.Success);
this.detectMount(arg.data.Provider); this.detectMount(arg.data.Provider);
this.removeMountsBusy(arg.data.Provider); this.removeMountsBusy(arg.data.Provider);
}; };
onUnmountDriveReply = (event, arg) => { onUnmountDriveReply = (_, arg) => {
if (arg && arg.data && !arg.data.Expected && arg.data.Location && this.props.ProviderState[arg.data.Provider].AutoRestart) { if (
arg &&
arg.data &&
!arg.data.Expected &&
arg.data.Location &&
this.props.ProviderState[arg.data.Provider].AutoRestart
) {
this.displayRetryMount(arg.data.Provider, arg.data.Remote, arg.data.Location); this.displayRetryMount(arg.data.Provider, arg.data.Remote, arg.data.Location);
} else { } else {
this.detectMount(arg.data.Provider); this.detectMount(arg.data.Provider);
@@ -292,140 +308,152 @@ class MountItems extends IPCContainer {
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
);
} }
}; };
render() { render() {
let retryDisplay; let retryDisplay;
if (this.state.DisplayRetry) { if (this.state.DisplayRetry) {
let retryList = []; const retryList = [];
let retryCount = 0; let retryCount = 0;
for (const provider in this.state.RetryItems) { Object.keys(this.state.RetryItems).forEach((provider) => {
if (this.state.RetryItems.hasOwnProperty(provider)) { if (this.state.RetryItems[provider].RetryMessage) {
if (this.state.RetryItems[provider].RetryMessage) { retryList.push(
retryList.push(<p key={'rl_' + retryList.length}>{this.state.RetryItems[provider].RetryMessage}</p>); <p key={'rl_' + retryList.length}>{this.state.RetryItems[provider].RetryMessage}</p>
} );
retryList.push(<Button clicked={() => this.cancelRetryMount(provider, () => this.detectMounts())}
key={'rl_' + retryList.length}>Cancel {provider} Remount
({this.state.RetryItems[provider].RetrySeconds}s)</Button>);
if (++retryCount < Object.keys(this.state.RetryItems).length) {
retryList.push(<div style={{paddingTop: 'var(--default_spacing)'}}
key={'rl_' + retryList.length}/>);
}
} }
} retryList.push(
<Button
clicked={() => this.cancelRetryMount(provider, () => this.detectMounts())}
key={'rl_' + retryList.length}>
Cancel {provider} Remount ({this.state.RetryItems[provider].RetrySeconds}s)
</Button>
);
if (++retryCount < Object.keys(this.state.RetryItems).length) {
retryList.push(
<div style={{ paddingTop: 'var(--default_spacing)' }} key={'rl_' + retryList.length} />
);
}
});
retryDisplay = ( retryDisplay = (
<Modal> <Modal>
<Box dxDark dxStyle={{padding: 'var(--default_spacing)', minWidth: '70vw'}}> <Box dxDark dxStyle={{ padding: 'var(--default_spacing)', minWidth: '70vw' }}>
<h1 style={{ <h1
textAlign: 'center', style={{
paddingBottom: 'var(--default_spacing)', textAlign: 'center',
color: 'var(--text_color_error)' paddingBottom: 'var(--default_spacing)',
}}>Mount Failed</h1> color: 'var(--text_color_error)',
}}>
Mount Failed
</h1>
{retryList} {retryList}
</Box> </Box>
</Modal> </Modal>
) );
} }
let footerItems = []; const 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(<div key={'hi_' + footerItems.length} style={{ height: '27px' }} />);
style={{height: '27px'}}/>);
} }
let items = []; const mountItems = [];
for (const provider of this.getProviderList(true)) { const addMountItem = (provider, remote, s3) => {
items.push(( if (mountItems.length > 0) {
<MountItem allowRemove={false} mountItems.push(
browseClicked={this.handleBrowseLocation} <div
changed={e => this.handleMountLocationChanged(provider, e.target.value)} key={'it_' + mountItems.length}
clicked={this.handleMountUnMount} style={{ paddingTop: 'calc(var(--default_spacing) * 2.5)' }}
key={'it_' + items.length} />
provider={provider}/> );
)); }
items.push(<div key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}} />) mountItems.push(
} <MountItem
allowRemove={remote || s3}
browseClicked={this.handleBrowseLocation}
changed={(e) => this.handleMountLocationChanged(provider, e.target.value)}
clicked={this.handleMountUnMount}
key={'it_' + mountItems.length}
provider={provider}
remote={remote}
s3={s3}
/>
);
};
this.getProviderList(true).forEach((provider) => addMountItem(provider));
if (this.props.remoteSupported) { if (this.props.remoteSupported) {
for (const provider of this.props.RemoteMounts) { this.props.RemoteMounts.forEach((provider) => addMountItem(provider, true));
items.push((
<MountItem allowRemove={true}
browseClicked={this.handleBrowseLocation}
changed={e => this.handleMountLocationChanged(provider, e.target.value)}
clicked={this.handleMountUnMount}
key={'it_' + items.length}
provider={provider}
remote/>
));
items.push(<div key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}}/>)
}
} }
if (this.props.s3Supported) { if (this.props.s3Supported) {
for (const provider of this.props.S3Mounts) { this.props.S3Mounts.forEach((provider) => addMountItem(provider, false, true));
items.push((
<MountItem allowRemove={true}
browseClicked={this.handleBrowseLocation}
changed={e => this.handleMountLocationChanged(provider, e.target.value)}
clicked={this.handleMountUnMount}
key={'it_' + items.length}
provider={provider}
s3/>
));
items.push(<div key={'it_' + items.length}
style={{paddingTop: 'var(--default_spacing)'}}/>)
}
} }
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={
{items} this.props.remoteSupported || this.props.s3Supported ? 'MountItemsRemote' : 'MountItems'
}>
{mountItems}
</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 +463,21 @@ 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) => dispatch(setAllowMount(provider, allow)),
setAutoMountProcessed: (provider, processed) => dispatch(setAutoMountProcessed(provider, processed)), 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) => dispatch(setMountState(provider, state)),
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)), setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
} };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(MountItems); export default connect(mapStateToProps, mapDispatchToProps)(MountItems);

View File

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

View File

@@ -1,43 +1,47 @@
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, setAllowDownload } from '../../redux/actions/download_actions';
downloadItem,
setAllowDownload
} 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 +49,10 @@ 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 +61,43 @@ 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(downloadItem(name, type, urls, isWinFSP, testVersion, appPlatform)),
setAllowDownload: allow => dispatch(setAllowDownload(allow)), notifyError: (msg) => dispatch(notifyError(msg)),
setInstallTestActive: active => dispatch(setInstallTestActive(active)), setAllowDownload: (allow) => dispatch(setAllowDownload(allow)),
setInstallTestActive: (active) => dispatch(setInstallTestActive(active)),
}; };
}; };

View File

@@ -1,59 +1,71 @@
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) => dispatch(notifyApplicationBusy(busy, true)),
notifyError: msg => dispatch(notifyError(msg)), notifyError: (msg) => dispatch(notifyError(msg)),
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)), notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg, true, 'skynet_export', 'json')),
} };
}; };
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(Constants.IPC_Grab_Skynet_Tree_Reply, this.onGrabSkynetTreeReply);
this.setRequestHandler(Constants.IPC_Export_Skylinks_Reply, this.onExportSkylinksReply); this.setRequestHandler(Constants.IPC_Export_Skylinks_Reply, this.onExportSkylinksReply);
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version}); 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 +73,250 @@ 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 icon={faSquare} fixedWidth style={{ padding: 0, margin: 0 }} />
expandAll: <FontAwesomeIcon icon={faPlusSquare} fixedWidth ),
style={{padding: 0, margin: 0}}/>, halfCheck: (
collapseAll: <FontAwesomeIcon icon={faMinusSquare} fixedWidth <FontAwesomeIcon
style={{padding: 0, margin: 0}}/>, icon={faHSquare}
parentClose: <FontAwesomeIcon icon={faFolder} fixedWidth
fixedWidth style={{ padding: 0, margin: 0 }}
color={'var(--heading_text_color)'} />
style={{padding: 0, margin: 0}}/>, ),
parentOpen: <FontAwesomeIcon icon={faFolderOpen} expandClose: (
fixedWidth <FontAwesomeIcon
color={'var(--heading_text_color)'} icon={faChevronRight}
style={{padding: 0, margin: 0}}/>, fixedWidth
leaf: <FontAwesomeIcon icon={faFile} style={{ padding: 0, margin: 0 }}
fixedWidth />
color={'var(--text_color)'} ),
style={{padding: 0, margin: 0}}/> expandOpen: (
}} <FontAwesomeIcon
nodes={this.state.nodes} icon={faChevronDown}
onClick={clicked => this.setState({clicked})} fixedWidth
onCheck={checked => this.setState({checked})} style={{ padding: 0, margin: 0 }}
onExpand={expanded => this.setState({expanded})}/> />
) ),
} expandAll: (
</div> <FontAwesomeIcon
<div style={{display: 'flex', justifyContent: 'flex-end'}}> icon={faPlusSquare}
{ fixedWidth
this.state.second_stage ? style={{ padding: 0, margin: 0 }}
<Button buttonStyles={{ />
),
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> : {this.state.second_stage ? 'Export' : 'Next'}
null </Button>
} </div>
<Button buttonStyles={{ </Box>
height: 'auto', );
marginLeft: 'var(--default_spacing)', }
marginTop: 'var(--default_spacing)',
width: 'auto'
}}
clicked={this.handleNavigation}>{this.state.second_stage ? 'Export' : 'Next'}</Button>
</div>
</Box>
);
} }
}); );

View File

@@ -1,32 +1,43 @@
import React from 'react' import React from 'react';
import './Import.css' import './Import.css';
import PropTypes from 'prop-types';
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>
); );
}; };
Import.propTypes = {
data: PropTypes.object,
};
export default Import; export default Import;

View File

@@ -1,32 +1,42 @@
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 PropTypes from 'prop-types';
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>
); );
}; };
ImportList.propTypes = {
imports_array: PropTypes.array.isRequired,
};
export default ImportList; export default ImportList;

View File

@@ -1,228 +1,277 @@
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, import { promptLocationAndReadFile } from '../../utils';
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) => dispatch(notifyApplicationBusy(busy, true)),
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(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(Constants.IPC_Import_Skylinks_Reply, this.onImportSkylinksReply);
imports_array: [], }
second_stage: false,
};
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 = () => { handleLoadFile = () => {
const msg = '!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' + const data = promptLocationAndReadFile(this.props.notifyError);
' AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA\n' + if (data) {
' AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg\n' + this.setState({ import_text: data });
'\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)
}
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 = []; handleNavigation = () => {
try { if (this.state.second_stage) {
for (let item of items) { try {
item = item.trim(); this.props.notifyApplicationBusy(true);
if (item.startsWith('[')) { this.sendRequest(Constants.IPC_Import_Skylinks, {
importsArray = JSON.parse(this.state.import_text.trim()); Version: this.props.version,
break; JsonArray: this.state.imports_array,
} else if (item.indexOf('=') >= 0) { });
const parts = item.split(',') } catch (e) {
let importItem = { this.props.notifyApplicationBusy(false);
directory: '/', this.props.notifyError(e);
skylink: '', }
token: '', } else {
}; const items = this.state.import_text.split('\n');
for (let part of parts) { let importsArray = [];
part = part.trim(); try {
const pair = part.split('='); for (let item of items) {
if (pair.length !== 2) { item = item.trim();
if (item.startsWith('[')) {
importsArray = JSON.parse(this.state.import_text.trim());
break;
} else if (item.indexOf('=') >= 0) {
const parts = item.split(',');
let importItem = {
directory: '/',
skylink: '',
token: '',
};
for (let part of parts) {
part = part.trim();
const pair = part.split('=');
if (pair.length !== 2) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
}
importItem = {
...importItem,
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''),
};
}
if (!importItem.skylink) {
throw new Error('Invalid syntax for import: directory="",skylink="",token=""'); throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
} }
importItem = { importsArray.push(importItem);
...importItem, } else if (item.length > 0) {
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''), 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) : '',
}, () => { imports_array: [],
if (failedImportsArray.length > 0) { second_stage: false,
this.props.notifyError(`Failed to import ${failedImportsArray.length} item(s)`); },
} else { () => {
this.props.notifyInfo('Import Result', `Successfully imported ${count} item(s)`); if (failedImportsArray.length > 0) {
} this.props.notifyError(`Failed to import ${failedImportsArray.length} item(s)`);
}); } else {
} else { this.props.notifyInfo('Import Result', `Successfully imported ${count} item(s)`);
this.props.notifyError(arg.data.Error); }
} }
}; );
} 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',
boxSizing: 'border-box',
display: 'block'
}}> }}>
<a href={'#'} <div
onClick={() => this.props.displaySkynetImport(false)} style={{
style={{cursor: 'pointer'}}>X</a> float: 'right',
</div> margin: 0,
<h1 padding: 0,
className={'SkynetImportHeading'}>{this.state.second_stage ? 'Verify Imports' : 'Import List'}</h1> marginTop: '-4px',
{ boxSizing: 'border-box',
this.state.second_stage ? ( display: 'block',
<ImportList imports_array={this.state.imports_array}/> }}>
<a
href={'#'}
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}
{!this.state.second_stage ? (
<Button
buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto',
}}
clicked={this.handleLoadFile}>
{'Import File...'}
</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> : {this.state.second_stage ? 'Import' : 'Next'}
null </Button>
} </div>
<Button buttonStyles={{
height: 'auto',
marginLeft: 'var(--default_spacing)',
marginTop: 'var(--default_spacing)',
width: 'auto'
}}
clicked={this.handleNavigation}>{this.state.second_stage ? 'Import' : 'Next'}</Button>
</div> </div>
</div> </Box>
</Box> );
); }
} }
}); );

View File

@@ -1,9 +1,10 @@
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 PropTypes from 'prop-types';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
export default class Password extends Component { class Password extends Component {
state = { state = {
button_text: 'clear', button_text: 'clear',
password: '', password: '',
@@ -12,14 +13,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 +28,7 @@ export default class Password extends Component {
password: this.props.value, password: this.props.value,
password2: '', password2: '',
show_password: false, show_password: false,
}) });
} }
} }
@@ -52,16 +53,19 @@ export default class Password extends Component {
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 +83,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 +97,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 +115,38 @@ export default class Password extends Component {
render() { render() {
return ( return (
<div className={'PasswordOwner'} style={{...this.props.style}}> <div className={'PasswordOwner'} style={{ ...this.props.style }}>
{ {this.props.readOnly ? null : (
this.props.readOnly ? null : <a href={'#'} <a href={'#'} className={'PasswordLink'} 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={this.state.button_text === 'confirm' ? this.state.password2 : this.state.password}
className={'PasswordShowHide'} />
onClick={this.handleShowHideClick}> <a href={'#'} className={'PasswordShowHide'} onClick={this.handleShowHideClick}>
<FontAwesomeIcon icon={this.state.show_password ? faEye : faEyeSlash} fixedWidth/> <FontAwesomeIcon icon={this.state.show_password ? faEye : faEyeSlash} fixedWidth />
</a> </a>
</div> </div>
); );
} }
}
Password.propTypes = {
autoFocus: PropTypes.bool,
changed: PropTypes.func,
disabled: PropTypes.bool,
mismatchHandler: PropTypes.func,
readOnly: PropTypes.bool,
style: PropTypes.object,
value: PropTypes.string,
}; };
export default Password;

File diff suppressed because it is too large Load Diff

View File

@@ -8,28 +8,30 @@ 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 +39,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

@@ -6,7 +6,7 @@
--control_border: 1px solid rgba(80, 80, 90, 0.9); --control_border: 1px solid rgba(80, 80, 90, 0.9);
--control_box_shadow: 2px 2px 2px black; --control_box_shadow: 2px 2px 2px black;
--control_transparent_background: rgba(10, 10, 16, 0.5); --control_transparent_background: rgba(10, 10, 16, 0.5);
--control_dark_transparent_background: rgba(10, 10, 16, 0.7); --control_dark_transparent_background: rgba(10, 10, 16, 0.75);
--text_color: rgba(200, 200, 240, 0.65); --text_color: rgba(200, 200, 240, 0.65);
--text_color_hover: rgba(200, 200, 225, 0.65); --text_color_hover: rgba(200, 200, 225, 0.65);
@@ -35,7 +35,8 @@ a {
font-weight: bold; font-weight: bold;
} }
html, body { html,
body {
height: 100%; height: 100%;
width: 100%; width: 100%;
margin: 0; margin: 0;
@@ -52,7 +53,9 @@ p {
text-align: center; text-align: center;
} }
h1, h2, h3 { h1,
h2,
h3 {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-weight: bold; font-weight: bold;
@@ -65,7 +68,8 @@ h1 {
color: var(--heading_text_color); color: var(--heading_text_color);
} }
h2, h3 { h2,
h3 {
color: var(--heading_other_text_color); color: var(--heading_other_text_color);
} }
@@ -81,19 +85,23 @@ p {
overflow-y: scroll; overflow-y: scroll;
} }
.scrollable-content, ::-webkit-scrollbar { .scrollable-content,
::-webkit-scrollbar {
width: 8px; width: 8px;
height: 8px; height: 8px;
} }
.scrollable-content, ::-webkit-scrollbar * { .scrollable-content,
::-webkit-scrollbar * {
background: transparent; background: transparent;
} }
.scrollable-content, ::-webkit-scrollbar-thumb { .scrollable-content,
::-webkit-scrollbar-thumb {
background: var(--control_background_hover) !important; background: var(--control_background_hover) !important;
} }
.scrollbar-corner, ::-webkit-scrollbar-corner { .scrollbar-corner,
::-webkit-scrollbar-corner {
background: transparent; background: transparent;
} }

View File

@@ -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');
@@ -44,17 +44,17 @@ 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,31 +1,15 @@
import { createAction } from '@reduxjs/toolkit';
import { createResponseDialogAction } from '../utils';
export const confirmYesNoAction = createResponseDialogAction('common', 'confirmYesNo');
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 = [];
export const confirmYesNo = title => { export const displaySelectAppPlatform = (display) => {
return dispatch => { return (dispatch) => {
return new Promise(resolve => {
dispatch(handleConfirmYesNo(true, title, resolve));
});
};
};
export const DISPLAY_CONFIRM_YES_NO = 'common/displayConfirmYesNo';
const displayConfirmYesNo = (show, title) => {
return {
type: DISPLAY_CONFIRM_YES_NO,
payload: {
show,
title
},
};
};
export const displaySelectAppPlatform = display => {
return dispatch => {
if (display) { if (display) {
dispatch(showWindow()); dispatch(showWindow());
} }
@@ -34,69 +18,47 @@ export const displaySelectAppPlatform = display => {
}; };
}; };
export const hideConfirmYesNo = confirmed => {
return dispatch => {
dispatch(handleConfirmYesNo(false, confirmed));
};
};
const handleConfirmYesNo = (show, titleOrConfirmed, resolve) => {
return dispatch => {
if (show) {
yesNoResolvers.push(resolve);
dispatch(displayConfirmYesNo(show, titleOrConfirmed));
} else {
yesNoResolvers[0](titleOrConfirmed);
yesNoResolvers.splice(0, 1);
dispatch(displayConfirmYesNo(false));
}
};
};
export const NOTIFY_APPLICATION_BUSY = 'common/notifyApplicationBusy'; 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 = {
Release: state.relver.Release, Release: state.relver.Release,
RemoteMounts: state.mounts.RemoteMounts, RemoteMounts: state.mounts.RemoteMounts,
S3Mounts: state.mounts.S3Mounts,
Version: state.relver.Version, Version: state.relver.Version,
}; };
const providerList = [ const providerList = [
...Constants.PROVIDER_LIST, ...Constants.PROVIDER_LIST,
...state.mounts.RemoteMounts, ...state.mounts.RemoteMounts,
...state.mounts.S3Mounts,
]; ];
for (const provider of providerList) { for (const provider of providerList) {
currentState[provider] = state.mounts.ProviderState[provider]; currentState[provider] = state.mounts.ProviderState[provider];
} }
if (ipcRenderer) { if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Save_State, { ipcRenderer.send(Constants.IPC_Save_State, { State: currentState });
State: currentState
});
} }
} }
}; };
@@ -106,7 +68,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 +78,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 +93,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,26 +1,21 @@
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');
@@ -32,27 +27,28 @@ export const downloadItem = (name, type, urls, isWinFSP, testVersion, appPlatfor
urls = [urls]; urls = [urls];
} }
const downloadComplete = result => { const downloadComplete = (result) => {
if (result.Success) { if (result.Success) {
switch (type) { switch (type) {
case Constants.INSTALL_TYPES.Dependency: case Constants.INSTALL_TYPES.Dependency:
dispatch(installDependency(result.Destination, result.URL, isWinFSP)); dispatch(installDependency(result.Destination, result.URL, isWinFSP));
break; break;
case Constants.INSTALL_TYPES.Release: case Constants.INSTALL_TYPES.Release:
dispatch(installRelease(result.Destination)); dispatch(installRelease(result.Destination));
break; break;
case Constants.INSTALL_TYPES.TestRelease: case Constants.INSTALL_TYPES.TestRelease:
dispatch(installAndTestRelease(result.Destination, testVersion, appPlatform)); dispatch(installAndTestRelease(result.Destination, testVersion, appPlatform));
break; break;
case Constants.INSTALL_TYPES.Upgrade: case Constants.INSTALL_TYPES.Upgrade:
//const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]]; // const info =
const sha256 = null;//info.sha256; // this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]];
const signature = null;//info.sig; //const sha256 = null; // info.sha256;
dispatch(installUpgrade(result.Destination, sha256, signature, !!result.SkipVerification)); //const signature = null; // info.sig;
break; dispatch(installUpgrade(result.Destination, null, null, !!result.SkipVerification));
default: break;
dispatch(notifyError('Unknown download type: ' + type)); default:
break; dispatch(notifyError('Unknown download type: ' + type));
break;
} }
} else { } else {
if (type === Constants.INSTALL_TYPES.TestRelease) { if (type === Constants.INSTALL_TYPES.TestRelease) {
@@ -62,10 +58,10 @@ 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));
@@ -76,7 +72,7 @@ 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(Constants.IPC_Download_File_Progress, downloadFileProgress);
if (!arg.data.Success && (++index < urls.length)) { if (!arg.data.Success && ++index < urls.length) {
downloadAtIndex(index); downloadAtIndex(index);
} else { } else {
downloadComplete(arg.data); downloadComplete(arg.data);

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));
@@ -50,33 +47,23 @@ export const notifyError = (msg, critical, callback) => {
}; };
}; };
export const notifyInfo = (title, msg) => { export const notifyInfo = (title, msg, saveToFile, fileName, extension) => {
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, saveToFile, fileName, extension));
}; };
}; };
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, saveToFile, fileName, extension) => {
return { return {
type: SET_INFO, type: SET_INFO,
payload: { payload: { title, msg, saveToFile, fileName, extension },
title, };
msg
}
}
}; };

View File

@@ -0,0 +1,3 @@
import { createResponseDialogAction } from '../utils';
export const addEditHostAction = createResponseDialogAction('host', 'displayAddEditHost');

View File

@@ -1,35 +1,34 @@
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 { import {
getIPCRenderer, confirmYesNoAction,
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 {
confirmYesNo,
displaySelectAppPlatform, displaySelectAppPlatform,
setAllowMount, setAllowMount,
setApplicationReady, setApplicationReady,
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';
@@ -40,7 +39,8 @@ 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,11 +57,13 @@ 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));
@@ -89,9 +91,12 @@ 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 (
state.relver.LocationsLookup[selectedVersion] &&
state.relver.LocationsLookup[selectedVersion].dependencies
) {
dependencies = state.relver.LocationsLookup[selectedVersion].dependencies; dependencies = state.relver.LocationsLookup[selectedVersion].dependencies;
} }
dispatch(checkInstalled(dependencies, selectedVersion)); dispatch(checkInstalled(dependencies, selectedVersion));
@@ -107,9 +112,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,10 +127,10 @@ export const installDependency = (source, url, isWinFSP) => {
}; };
if (result.Success && source.toLowerCase().endsWith('.dmg')) { if (result.Success && source.toLowerCase().endsWith('.dmg')) {
const dep = getState().install.MissingDependencies.find(d => { const dep = getState().install.MissingDependencies.find((d) => {
return d.download === url; return d.download === url;
}); });
const i = setInterval(()=> { const i = setInterval(() => {
const ret = ipcRenderer.sendSync(Constants.IPC_Check_Dependency_Installed + '_sync', { const ret = ipcRenderer.sendSync(Constants.IPC_Check_Dependency_Installed + '_sync', {
File: dep.file, File: dep.file,
}); });
@@ -160,7 +165,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,7 +182,7 @@ 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);
@@ -201,26 +206,26 @@ export const installReleaseByVersion = (release, version) => {
}; };
if (getState().mounts.MountsBusy) { if (getState().mounts.MountsBusy) {
dispatch(confirmYesNo('Unmount all drives?')) dispatch(confirmYesNoAction.display(true, null, { title: 'Unmount all drives?' }))
.then(confirmed => { .then(({ changed }) => {
if (confirmed) { if (changed) {
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,
}); });
@@ -249,19 +254,26 @@ 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
)
);
} }
}; };

View File

@@ -1,10 +1,10 @@
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 { confirmYesNoAction, saveState } from './common_actions';
import {notifyError} from './error_actions'; import { notifyError } from './error_actions';
export const addRemoteMount = (hostNameOrIp, port, token) => { export const addRemoteMount = (hostNameOrIp, port, token) => {
return (dispatch, getState) => { return (dispatch, getState) => {
@@ -23,18 +23,17 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
Version: getState().relver.InstalledVersion, Version: getState().relver.InstalledVersion,
}); });
} else { } else {
dispatch( dispatch(notifyError('Failed to create remote mount: ' + arg.data.Error));
notifyError('Failed to set \'RemoteToken\': ' + arg.data.Error));
dispatch(setBusy(false)); dispatch(setBusy(false));
} }
}); });
ipcRenderer.send(Constants.IPC_Set_Config_Values, { ipcRenderer.send(Constants.IPC_Set_Config_Values, {
Items: [ Items: [
{Name: 'RemoteMount.RemoteHostNameOrIp', Value: hostNameOrIp}, { Name: 'RemoteMount.RemoteHostNameOrIp', Value: hostNameOrIp },
{Name: 'RemoteMount.RemoteToken', Value: token}, { Name: 'RemoteMount.RemoteToken', Value: token },
{Name: 'RemoteMount.RemotePort', Value: port}, { Name: 'RemoteMount.RemotePort', Value: port.toString() },
{Name: 'RemoteMount.IsRemoteMount', Value: 'true'}, { Name: 'RemoteMount.IsRemoteMount', Value: 'true' },
], ],
Provider: provider, Provider: provider,
Remote: true, Remote: true,
@@ -59,19 +58,18 @@ export const addS3Mount = (name, accessKey, secretKey, region, bucketName, url)
Version: getState().relver.InstalledVersion, Version: getState().relver.InstalledVersion,
}); });
} 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));
} }
}); });
ipcRenderer.send(Constants.IPC_Set_Config_Values, { ipcRenderer.send(Constants.IPC_Set_Config_Values, {
Items: [ Items: [
{Name: 'S3Config.AccessKey', Value: accessKey}, { Name: 'S3Config.AccessKey', Value: accessKey },
{Name: 'S3Config.SecretKey', Value: secretKey}, { Name: 'S3Config.SecretKey', Value: secretKey },
{Name: 'S3Config.Region', Value: region}, { Name: 'S3Config.Region', Value: region },
{Name: 'S3Config.BucketName', Value: bucketName}, { Name: 'S3Config.BucketName', Value: bucketName },
{Name: 'S3Config.URL', Value: url}, { Name: 'S3Config.URL', Value: url },
], ],
Provider: provider, Provider: provider,
S3: true, S3: true,
@@ -95,20 +93,23 @@ 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 => { confirmYesNoAction.display(true, null, {
if (confirmed) { title: 'Delete [' + provider.substr(isRemote ? 6 : 2) + ']?',
})
).then(({ changed }) => {
if (changed) {
dispatch(removeMount2(provider)); dispatch(removeMount2(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 +120,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,44 +129,38 @@ 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) => {
return {type: SET_AUTO_MOUNT_PROCESSED, payload: {provider, processed}}; return { type: SET_AUTO_MOUNT_PROCESSED, payload: { 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 +168,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,18 @@
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 {notifyError} from './error_actions';
import {
saveState,
setAllowMount,
setApplicationReady,
showWindow
} from './common_actions';
import {
checkVersionInstalled,
setDismissDependencies
} from './install_actions';
import {unmountAll} from './mount_actions';
import { import {
checkNewReleases, checkNewReleases,
getIPCRenderer, getIPCRenderer,
getNewReleases, getSelectedVersionFromState getNewReleases,
} from '../../utils'; getSelectedVersionFromState,
} from '../../utils.jsx';
import { saveState, setAllowMount, setApplicationReady, showWindow } from './common_actions';
import { notifyError } from './error_actions';
import { checkVersionInstalled, setDismissDependencies } from './install_actions';
import { unmountAll } from './mount_actions';
export const CLEAR_UI_UPGRADE = 'relver/clearUIUpgrade'; export const CLEAR_UI_UPGRADE = 'relver/clearUIUpgrade';
export const clearUIUpgrade = () => { export const clearUIUpgrade = () => {
@@ -27,12 +22,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,32 +37,40 @@ 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) {
@@ -78,19 +81,21 @@ export const loadReleases = () => {
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 = versionLookup[Constants.RELEASE_TYPES[release]].length - 1;
} else if ((version === -1) || !versionLookup[Constants.RELEASE_TYPES[release]][version]) { } 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 +103,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 +122,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 +136,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 +158,15 @@ 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 +183,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 +197,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) {
@@ -200,14 +214,14 @@ export const setNewReleasesAvailable = createAction('relver/setNewReleasesAvaila
export const setNewReleasesAvailable2 = createAction('relver/setNewReleasesAvailable2'); 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');
@@ -219,6 +233,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, confirmYesNoAction,
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 { [confirmYesNoAction.action_type]: (state, action) => {
...state, return {
DisplaySelectAppPlatform: action.payload, ...state,
} DisplayConfirmYesNo: action.payload.display,
}, ConfirmTitle:
[setAllowMount]: (state, action) => { action.payload.display && action.payload.data ? action.payload.data.title : null,
return { };
...state, },
AllowMount: action.payload, [SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => {
} return { ...state, DisplaySelectAppPlatform: action.payload };
}, },
[setApplicationReady]: (state, action) => { [setAllowMount]: (state, action) => {
return { return { ...state, AllowMount: action.payload };
...state, },
AppReady: action.payload, [setApplicationReady]: (state, action) => {
}; return {
}, ...state,
[setLinuxAppPlatform]: (state, action) => { AppReady: action.payload,
return { };
...state, },
AppPlatform: action.payload, [setLinuxAppPlatform]: (state, action) => {
} 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,52 @@
import {createReducer} from '@reduxjs/toolkit'; import { createReducer } from '@reduxjs/toolkit';
import { import { CLEAR_ERROR, CLEAR_INFO, SET_ERROR_INFO, SET_INFO } from '../actions/error_actions';
CLEAR_ERROR,
CLEAR_INFO,
SET_ERROR_INFO,
SET_INFO
} 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.ErrorStack.length > 0 ? state.ErrorStack.slice(1) : [];
...state, return {
DisplayInfo: (infoStack.length > 0), ...state,
InfoStack: infoStack, DisplayError: errorStack.length > 0,
} ErrorStack: errorStack,
}, };
[SET_ERROR_INFO]: (state, action) => { },
const errorStack = [action.payload.msg, ...state.ErrorStack]; [CLEAR_INFO]: (state) => {
return { const infoStack = state.InfoStack.length > 0 ? state.InfoStack.slice(1) : [];
...state, return {
DisplayError: true, ...state,
ErrorCritical: state.ErrorCritical || action.payload.critical, DisplayInfo: infoStack.length > 0,
ErrorStack: errorStack, InfoStack: infoStack,
} };
}, },
[SET_INFO]: (state, action) => { [SET_ERROR_INFO]: (state, action) => {
const infoStack = [{ const errorStack = [action.payload.msg, ...state.ErrorStack];
title: action.payload.title, return {
message: action.payload.msg ...state,
}, ...state.InfoStack]; DisplayError: true,
return { ErrorCritical: state.ErrorCritical || action.payload.critical,
...state, ErrorStack: errorStack,
DisplayInfo: true, };
InfoStack: infoStack, },
} [SET_INFO]: (state, action) => {
const infoStack = [
{
title: action.payload.title,
message: action.payload.msg,
saveToFile: action.payload.saveToFile,
fileName: action.payload.fileName,
extension: action.payload.extension,
},
...state.InfoStack,
];
return { ...state, DisplayInfo: true, InfoStack: infoStack };
},
} }
}); );

View File

@@ -0,0 +1,20 @@
import { createReducer } from '@reduxjs/toolkit';
import { addEditHostAction } from '../actions/host_actions';
export const hostReducer = createReducer(
{
DisplayAddEditHost: false,
HostData: {},
HostList: [],
},
{
[addEditHostAction.action_type]: (state, action) => {
return {
...state,
DisplayAddEditHost: action.payload.display,
HostData: action.payload.data ? action.payload.data.host_data || {} : {},
HostList: action.payload.data ? action.payload.data.host_list || [] : [],
};
},
}
);

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,17 @@ 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((i) => i !== action.payload);
state.RemoteMounts.filter(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,
@@ -149,8 +151,8 @@ export const createMountReducer = state => {
S3Mounts: s3Mounts, S3Mounts: s3Mounts,
}; };
}, },
[RESET_MOUNTS_STATE]: (state, action) => { [RESET_MOUNTS_STATE]: (state) => {
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 +160,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 +171,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 +185,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 +198,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 +209,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,18 +1,21 @@
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';
import { hostReducer } from '../reducers/host_reducer';
export default function createAppStore(platformInfo, version, state) { export default function createAppStore(platformInfo, version, state) {
const reducer = { const reducer = {
common: createCommonReducer(platformInfo, version), common: createCommonReducer(platformInfo, version),
download: downloadReducer, download: downloadReducer,
error: errorReducer, error: errorReducer,
host: hostReducer,
install: installReducer, install: installReducer,
mounts: createMountReducer(state), mounts: createMountReducer(state),
relver: releaseVersionReducer, relver: releaseVersionReducer,
@@ -25,6 +28,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',
}); });
} }

47
src/redux/utils.js Normal file
View File

@@ -0,0 +1,47 @@
export const createResponseDialogAction = (type, name) => {
let resolverList = [];
const display = (show, cb, data) => {
return (dispatch) => {
if (cb) {
dispatch(display(show, null, data)).then(({ changed, data }) => cb(changed, data));
} else {
return new Promise((resolve) => {
dispatch(handleDisplay(show, data, resolve));
});
}
};
};
const handleDisplay = (show, data, resolve) => {
return (dispatch) => {
if (show) {
resolverList.push(resolve);
dispatch(displayAction(show, data));
} else {
dispatch(complete(false));
}
};
};
const complete = (changed, data) => {
return (dispatch) => {
if (changed) {
resolverList[0]({ changed, data });
}
resolverList.splice(0, 1);
dispatch(displayAction(false));
};
};
const DISPLAY_ACTION = type + '/' + name;
const displayAction = (display, data) => {
return { type: DISPLAY_ACTION, payload: { display, data } };
};
return {
action_type: DISPLAY_ACTION,
complete,
display,
};
};

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,55 @@
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, {}, data.Code);
} }
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, 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 +58,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,7 +13,7 @@ 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,
}, },
}); });
@@ -30,9 +30,7 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
}; };
} catch (e) { } catch (e) {
event.returnValue = { event.returnValue = {
data: { data: { Exists: false },
Exists: false
},
}; };
} }
}); });
@@ -40,56 +38,71 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
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 +110,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,31 +1,86 @@
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_Browse_File + '_sync', (event, data) => {
dialog.showOpenDialog(
getMainWindow(),
{
defaultPath: data.Location,
properties: ['openFile'],
title: data.Title,
},
(filePaths) => {
if (filePaths && filePaths.length > 0) {
event.returnValue = filePaths[0];
} else {
event.returnValue = '';
}
}
);
});
ipcMain.on(Constants.IPC_Delete_File, (_, data) => {
try { try {
if (fs.existsSync(data.FilePath)) { if (fs.existsSync(data.FilePath)) {
fs.unlinkSync(data.FilePath); fs.unlinkSync(data.FilePath);
} }
} catch (e) { } catch (e) {}
});
ipcMain.on(Constants.IPC_Select_File + '_sync', (event, data) => {
dialog.showSaveDialog(
getMainWindow(),
{
defaultPath: data.Location,
properties: ['createDirectory', 'showOverwriteConfirmation'],
title: data.Title,
},
(filePath) => {
if (filePath && filePath.length > 0) {
event.returnValue = filePath;
} else {
event.returnValue = '';
}
}
);
});
ipcMain.on(Constants.IPC_Read_File + '_sync', (event, data) => {
try {
const contents = fs.readFileSync(data.Location, 'utf8').toString();
event.returnValue = { success: true, contents };
} catch (err) {
event.returnValue = { success: false, error: err.toString() };
}
});
ipcMain.on(Constants.IPC_Save_File + '_sync', (event, data) => {
try {
fs.writeFileSync(data.Location, data.Data, 'utf8');
event.returnValue = { success: true };
} catch (err) {
event.returnValue = { success: false, error: err.toString() };
} }
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -10,7 +10,7 @@ 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];
@@ -20,28 +20,28 @@ const clearManualMountDetection = 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);
}; };
@@ -62,12 +62,9 @@ const unmountAllDrives = () => {
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()) {
@@ -90,11 +87,7 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
const provider = data.Provider; const provider = data.Provider;
let driveLetters = {}; let driveLetters = {};
const providerList = [ const providerList = [...Constants.PROVIDER_LIST, ...data.RemoteMounts, ...data.S3Mounts];
...Constants.PROVIDER_LIST,
...data.RemoteMounts,
...data.S3Mounts,
];
for (const provider of providerList) { for (const provider of providerList) {
driveLetters[provider] = []; driveLetters[provider] = [];
} }
@@ -106,8 +99,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 +109,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 +128,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 +222,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 +263,26 @@ 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 +292,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,45 @@
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(data.Path, data.Version, data.Provider, data.Remote, data.S3)
.then(data => { .then((data) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, { standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
Items: data.items, Items: data.items,
});
})
.catch((e) => {
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(data.Path, data.Version, data.Provider, data.Remote, data.S3)
.then(data => { .then((data) => {
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, { standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
Items: data.items, Items: data.items,
});
})
.catch((e) => {
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(data.Path, data.Pinned, data.Version, data.Provider, data.Remote, data.S3)
.then(success => { .then((success) => {
event.returnValue = success; event.returnValue = success;
}) })
.catch(e => { .catch((e) => {
event.returnValue = false; event.returnValue = false;
}); });
}); });
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

View File

@@ -10,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,39 @@ 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(Constants.LINUX_DETECT_SCRIPT_URL, scriptFile, null, (err) => {
.downloadFile(Constants.LINUX_DETECT_SCRIPT_URL, scriptFile, null, err => { if (err) {
if (err) {
sendResponse(appPlatform, platform);
} else {
helpers
.executeScript(scriptFile)
.then(data => {
appPlatform = data.replace(/(\r\n|\n|\r)/gm, "");
sendResponse(appPlatform, platform); sendResponse(appPlatform, platform);
}) } else {
.catch(() => { helpers
sendResponse(appPlatform, platform); .executeScript(scriptFile)
}); .then((data) => {
} appPlatform = data.replace(/(\r\n|\n|\r)/gm, '');
}); sendResponse(appPlatform, platform);
} else { })
sendResponse(appPlatform, platform); .catch(() => {
} sendResponse(appPlatform, platform);
}) });
.catch(() => { }
sendResponse(platform, platform); });
}); } else {
sendResponse(appPlatform, platform);
}
})
.catch(() => {
sendResponse(platform, platform);
});
} }
} else { } else {
sendResponse(platform, platform); sendResponse(platform, platform);
@@ -75,5 +74,5 @@ const addListeners = (ipcMain, {detectScript, saveUiSettings}) => {
module.exports = { module.exports = {
getPlatformOverride, getPlatformOverride,
setPlatformOverride, setPlatformOverride,
addListeners addListeners,
}; };

View File

@@ -5,28 +5,33 @@ 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 = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
} catch (e) { } catch (e) {}
} standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, { Dependencies: dependencies,
Dependencies: dependencies, Exists: exists,
Exists: exists, Version: data.Version,
Version: data.Version, });
})
.catch((error) => {
standardIPCReply(
event,
Constants.IPC_Check_Installed_Reply,
{
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 +49,59 @@ 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,16 +1,16 @@
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 => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {}, error); standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {}, error);
}); });
}); });
@@ -18,12 +18,12 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
ipcMain.on(Constants.IPC_Grab_Skynet_Tree, (event, data) => { ipcMain.on(Constants.IPC_Grab_Skynet_Tree, (event, data) => {
helpers helpers
.grabSkynetFileTree(data.Version) .grabSkynetFileTree(data.Version)
.then(result => { .then((result) => {
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, { standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {
Result: result, Result: result,
}); });
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {}, error); standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {}, error);
}); });
}); });
@@ -31,17 +31,30 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
ipcMain.on(Constants.IPC_Import_Skylinks, (event, data) => { ipcMain.on(Constants.IPC_Import_Skylinks, (event, data) => {
helpers helpers
.importSkylinks(data.Version, data.JsonArray) .importSkylinks(data.Version, data.JsonArray)
.then(result => { .then((result) => {
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, { standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {
Result: result, Result: result,
}); });
}) })
.catch(error => { .catch((error) => {
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {}, error); standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {}, error);
}); });
}); });
ipcMain.on(Constants.IPC_Skynet_Test_Logon, (event, data) => {
helpers
.testSkynetLogon(data.Version, data.AuthURL, data.AuthUser, data.AuthPassword)
.then((success) => {
if (success) {
standardIPCReply(event, Constants.IPC_Skynet_Test_Logon_Reply, {});
} else {
standardIPCReply(event, Constants.IPC_Skynet_Test_Logon_Reply, {}, 'Logon failed. Please check credentials');
}
})
.catch((error) => {
standardIPCReply(event, Constants.IPC_Skynet_Test_Logon_Reply, {}, error);
});
});
}; };
module.exports = { module.exports = { addListeners };
addListeners
};

Some files were not shown because too many files have changed in this diff Show More