Merged 1.1.x_branch into master

This commit is contained in:
2020-01-08 11:30:28 -06:00
102 changed files with 1547 additions and 755 deletions

View File

@@ -1,8 +1,29 @@
# Changelog # Changelog
## 1.1.2
* Style changes
## 1.1.1
* \#43: Support for remote UNIX mounts
* Improved mount state detection
## 1.1.0
* \#40: Support for remote Windows mounts
* \#42: Failing to unmount on OS X
* Allow enabling dev tools (Ctrl-Shift-I or F12)
* ScPrime re-brand
* Ubuntu 19.10 support
* Linux Mint 19.2 support
* Fedora 31 support
## 1.0.11 (Linux only) ## 1.0.11 (Linux only)
* Fix Ubuntu 19.10 detection * Fix Ubuntu 19.10 detection
## 1.0.10 (Windows only) ## 1.0.10
* Fix VC runtime detection (detect additional GUID's)
## 1.0.9 (Alpha Testing Release)
* \#40: Support for remote Windows mounts
* ScPrime re-brand
* Fix VC runtime detection (detect additional GUID's) * Fix VC runtime detection (detect additional GUID's)
## 1.0.8 ## 1.0.8

View File

@@ -1,18 +1,18 @@
# Repertory UI # Repertory UI
![alt text](https://i.ibb.co/s6Qpnwk/Screen-Shot-2019-08-31-at-2-14-48-AM.png) ![alt text](https://i.ibb.co/7Xwcqbd/repertor-ui-1-1-1.png)
## GUI for [Repertory](https://bitbucket.org/blockstorage/repertory) ## GUI for [Repertory](https://bitbucket.org/blockstorage/repertory)
Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows. Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.
## Requirements ## Requirements
* Sia >=1.4.1 * Sia >=1.4.1
* SiaPrime >=1.4.0 * ScPrime >=1.4.1.2
## Downloads ## Downloads
* **Repertory UI v1.0.11 Linux 64-bit** [<Primary\>](https://pixeldrain.com/u/cP4E5Aia) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage) * **Repertory UI v1.1.2 Linux 64-bit** [<Primary\>](https://pixeldrain.com/u/5i1mA1gb) [<Alternate\>](https://bitbucket.org/blockstorage/repertory/downloads/repertory-ui_1.1.2_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.0.8 OS X 64-bit** [<Primary\>](https://pixeldrain.com/u/6NjT6uEl) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg) * **Repertory UI v1.1.2 OS X 64-bit** [<Primary\>](https://pixeldrain.com/u/jEWmNDRX) [<Alternate\>](https://bitbucket.org/blockstorage/repertory/downloads/repertory-ui_1.1.2_mac.dmg)
* **Repertory UI v1.0.10 Windows 64-bit** [<Primary\>](https://pixeldrain.com/u/YCzKB0a9) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.10_win.exe) * **Repertory UI v1.1.2 Windows 64-bit** [<Primary\>](https://pixeldrain.com/u/TkQn25Bm) [<Alternate\>](https://bitbucket.org/blockstorage/repertory/downloads/repertory-ui_1.1.2_win.exe)
## Supported Platforms ## Supported Platforms
* OS X 64-bit * OS X 64-bit
@@ -29,8 +29,10 @@ Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions v
* Fedora 28 * Fedora 28
* Fedora 29 * Fedora 29
* Fedora 30 * Fedora 30
* Fedora 31
* Linux Mint 19 * Linux Mint 19
* Linux Mint 19.1 * Linux Mint 19.1
* Linux Mint 19.2
* Manjaro * Manjaro
* Uses `Ubuntu 18.10` binaries for compatibility * Uses `Ubuntu 18.10` binaries for compatibility
* OpenSUSE Leap 15.0 * OpenSUSE Leap 15.0
@@ -40,12 +42,18 @@ Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions v
* Ubuntu 18.04 * Ubuntu 18.04
* Ubuntu 18.10 * Ubuntu 18.10
* Ubuntu 19.04 * Ubuntu 19.04
* Ubuntu 19.10 * Ubuntu 19.10
* Uses `Ubuntu 19.04` binaries for compatibility
## Issues/Suggestions ## Issues/Suggestions
Please submit [here](https://bitbucket.org/blockstorage/repertory-ui/issues?status=new&status=open) Please submit [here](https://bitbucket.org/blockstorage/repertory-ui/issues?status=new&status=open)
## Remote Mount
This feature allows you to share your mounts with other PC's and mount them remotely.
### Enabling Remote Mount for Sharing
[Watch Demo](src/assets/images/Enable_Remote_Mount.gif)
### Connecting to a Remote Mount
[Watch Demo](src/assets/images/Connect_To_Remote.gif)
## The FAQ of One ## The FAQ of One
### My distro isn't listed... Why? ### My distro isn't listed... Why?
It may still function properly as long as it derives from one listed above. I do plan to test more, so this list should grow over time. It may still function properly as long as it derives from one listed above. I do plan to test more, so this list should grow over time.

View File

@@ -26,6 +26,8 @@ pushd "%ROOT%"
) )
set OUT_FILE=repertory-ui_%APP_VER%_win.exe set OUT_FILE=repertory-ui_%APP_VER%_win.exe
call npm install
call npm run dist && ( call npm run dist && (
pushd dist pushd dist
(certutil -hashfile "%OUT_FILE%" SHA256 | %SED_BIN% -e "1d" -e "$d" -e "s/\ //g") > "%OUT_FILE%.sha256" || (call :ERROR "Create sha-256 failed") (certutil -hashfile "%OUT_FILE%" SHA256 | %SED_BIN% -e "1d" -e "$d" -e "s/\ //g") > "%OUT_FILE%.sha256" || (call :ERROR "Create sha-256 failed")

View File

@@ -19,7 +19,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="arch centos7 debian9 debian10 fedora28 fedora29 fedora30 opensuse15 opensuse15.1 solus tumbleweed ubuntu18.04 ubuntu18.10 ubuntu19.04" DISTRO_LIST="arch centos7 debian9 debian10 fedora28 fedora29 fedora30 fedora31 opensuse15 opensuse15.1 solus tumbleweed ubuntu18.04 ubuntu18.10 ubuntu19.04 ubuntu19.10"
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
@@ -38,6 +38,8 @@ upload_to_bitbucket() {
chmod +x "bin/${JQ_EXEC}" || exit_script "chmod +x ${JQ_EXEC} failed" chmod +x "bin/${JQ_EXEC}" || exit_script "chmod +x ${JQ_EXEC} failed"
npm install
if npm run dist; then if npm run dist; then
cd dist cd dist
${SHA256_EXEC} ${OUT_FILE} > ${OUT_FILE}.sha256 || exit_script "Create sha256 failed" ${SHA256_EXEC} ${OUT_FILE} > ${OUT_FILE}.sha256 || exit_script "Create sha256 failed"

BIN
images/blue/icon_24X24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

BIN
images/blue/icon_40x40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

BIN
images/blue/icon_48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

BIN
images/blue/logo.icns Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
images/green/icon_24X24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
images/green/icon_40x40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
images/green/icon_48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
images/green/logo.icns Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
images/logo_blue.xcf Normal file

Binary file not shown.

BIN
images/logo_green.xcf Normal file

Binary file not shown.

BIN
logo.xcf

Binary file not shown.

View File

@@ -1,37 +1,38 @@
{ {
"name": "repertory-ui", "name": "repertory-ui",
"version": "1.0.11", "version": "1.1.2",
"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 and/or SiaPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.", "description": "GUI for Repertory - Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.22", "@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-solid-svg-icons": "^5.10.2", "@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-fontawesome": "^0.1.4", "@fortawesome/react-fontawesome": "^0.1.7",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"axios": "^0.18.1", "axios": "^0.19.0",
"electron-debug": "^2.2.0", "devtron": "^1.4.0",
"electron-debug": "^3.0.1",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"node-schedule": "^1.3.2", "node-schedule": "^1.3.2",
"randomstring": "^1.1.5", "randomstring": "^1.1.5",
"react": "^16.9.0", "react": "^16.11.0",
"react-dom": "^16.9.0", "react-dom": "^16.11.0",
"react-loader-spinner": "^2.3.0", "react-loader-spinner": "^3.1.4",
"react-redux": "^7.1.1", "react-redux": "^7.1.3",
"react-scripts": "2.1.8", "react-scripts": "3.2.0",
"react-tooltip": "^3.10.0", "react-tooltip": "^3.11.1",
"redux": "^4.0.4", "redux": "^4.0.4",
"redux-starter-kit": "^0.5.1", "redux-starter-kit": "^1.0.1",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"unzipper": "^0.9.15", "unzipper": "^0.10.5",
"winreg": "^1.2.4" "winreg": "^1.2.4"
}, },
"devDependencies": { "devDependencies": {
"cross-env": "^5.2.0", "cross-env": "^6.0.3",
"electron": "^4.2.9", "electron": "^5.0.12",
"electron-builder": "^20.44.4", "electron-builder": "^20.44.4",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"typescript": "^3.5.3", "typescript": "^3.7.2",
"webpack-browser-plugin": "^1.0.20" "webpack-browser-plugin": "^1.0.20"
}, },
"scripts": { "scripts": {
@@ -70,8 +71,7 @@
"node_modules/**/*", "node_modules/**/*",
"src/helpers.js", "src/helpers.js",
"src/renderer/**/*", "src/renderer/**/*",
"public/detect_linux.sh", "public/detect_linux.sh"
"public/install_linux.sh"
], ],
"linux": { "linux": {
"category": "Utility", "category": "Utility",
@@ -79,9 +79,9 @@
}, },
"mac": { "mac": {
"category": "public.app-category.utilities", "category": "public.app-category.utilities",
"icon": "./build/icon_color.icns",
"target": "dmg", "target": "dmg",
"darkModeSupport": true "darkModeSupport": true,
"icon": "./build/icon.icns"
}, },
"win": { "win": {
"icon": "./build/icon.ico", "icon": "./build/icon.ico",

View File

@@ -18,17 +18,11 @@ if [ -f /etc/centos-release ]; then
fi fi
elif [ -f /etc/fedora-release ]; then elif [ -f /etc/fedora-release ]; then
. /etc/os-release . /etc/os-release
if [ "$VERSION_ID" = "30" ]; then if [ "$VERSION_ID" != "31" ] && [ "$VERSION_ID" != "30" ] && [ "$VERSION_ID" != "29" ] && [ "$VERSION_ID" != "28" ]; then
DISTNAME=fedora
DISTVER=30
elif [ "$VERSION_ID" = "29" ]; then
DISTNAME=fedora
DISTVER=29
elif [ "$VERSION_ID" = "28" ]; then
DISTNAME=fedora
DISTVER=28
else
resetDistVer resetDistVer
else
DISTNAME=fedora
DISTVER=$VERSION_ID
fi fi
elif [ -f /etc/solus-release ]; then elif [ -f /etc/solus-release ]; then
DISTNAME=solus DISTNAME=solus
@@ -38,7 +32,7 @@ elif [ -f /etc/lsb-release ]; then
DISTVER=${DISTRIB_RELEASE} DISTVER=${DISTRIB_RELEASE}
if [ "$DISTNAME" != "ubuntu" ]; then if [ "$DISTNAME" != "ubuntu" ]; then
if [ "$DISTNAME" = "linuxmint" ]; then if [ "$DISTNAME" = "linuxmint" ]; then
if [ "$DISTVER" = "19" ] || [ "$DISTVER" = "19.1" ]; then if [ "$DISTVER" = "19" ] || [ "$DISTVER" = "19.1" ] || [ "$DISTVER" = "19.2" ]; then
DISTNAME=ubuntu DISTNAME=ubuntu
DISTVER=18.04 DISTVER=18.04
else else
@@ -54,18 +48,18 @@ elif [ -f /etc/lsb-release ]; then
else else
resetDistVer resetDistVer
fi fi
elif [ "$DISTVER" = "19.10" ]; then elif [ "$DISTVER" != "18.04" ] && [ "$DISTVER" != "18.10" ] && [ "$DISTVER" != "19.04" ] && [ "$DISTVER" != "19.10" ]; then
DISTVER=19.04 resetDistVer
fi fi
elif [ -f /etc/debian_version ]; then fi
if [ "$DISTNAME" = "unknown" ] && [ -f /etc/debian_version ]; then
DISTNAME=debian DISTNAME=debian
DISTVER=$(head -1 /etc/debian_version|awk -F. '{print $1}') DISTVER=$(head -1 /etc/debian_version|awk -F. '{print $1}')
if [ "$DISTVER" != "9" ]; then if [ "$DISTVER" != "9" ] && [ "$DISTVER" != "10" ]; then
if [ "$DISTVER" != "10" ]; then resetDistVer
resetDistVer
fi
fi fi
fi fi
if [ "$DISTNAME" = "unknown" ]; then if [ "$DISTNAME" = "unknown" ]; then
if [ -f /etc/os-release ]; then if [ -f /etc/os-release ]; then
@@ -96,4 +90,4 @@ if [ "$DISTNAME" = "unknown" ]; then
fi fi
fi fi
echo ${DISTNAME}${DISTVER} echo ${DISTNAME}${DISTVER}

View File

@@ -14,7 +14,12 @@ const helpers = require('../src/helpers');
const os = require('os'); const os = require('os');
const path = require('path'); const path = require('path');
const url = require('url'); const url = require('url');
require('electron-debug/index')(); const debug = require('electron-debug');
debug({
devToolsMode: 'undocked',
isEnabled: true,
showDevTools: !!process.env.ELECTRON_START_URL,
});
require.extensions['.sh'] = function (module, filename) { require.extensions['.sh'] = function (module, filename) {
module.exports = fs.readFileSync(filename, 'utf8'); module.exports = fs.readFileSync(filename, 'utf8');
@@ -34,6 +39,13 @@ const StateIPC = require('../src/renderer/ipc/StateIPC');
const SystemIPC = require('../src/renderer/ipc/SystemIPC'); const SystemIPC = require('../src/renderer/ipc/SystemIPC');
const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC'); const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC');
const platform = os.platform();
const dimensions = {
height: ((platform === 'darwin') ? 346 : (platform === 'win32') ? 326 : 300),
width: 428 + ((platform === 'win32') ? 40 : (platform === 'darwin') ? 190 : 200),
};
let isShutdown = false; let isShutdown = false;
let isQuiting = false; let isQuiting = false;
let isInstalling = false; let isInstalling = false;
@@ -70,28 +82,32 @@ const createWindow = () => {
loadUiSettings(); loadUiSettings();
let extra = {}; let extra = {};
if (os.platform() === 'linux') { if (platform === 'linux') {
extra = { extra = {
icon: path.join(__dirname, '../', 'icon.png'), icon: path.join(__dirname, '../build/', 'logo.png'),
} }
} }
// Create the browser window. // Create the browser window.
const height = (process.env.ELECTRON_START_URL || (os.platform() === 'darwin') ? 294 : 274) - ((os.platform() === 'win32') ? 0 : 20);
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 428 + ((os.platform() === 'win32') ? 0 : (os.platform() === 'darwin') ? 150 : 160), height: dimensions.height,
height: height, width: dimensions.width,
fullscreen: false, fullscreen: false,
resizable: false, resizable: false,
show: !launchHidden, show: !launchHidden,
title: 'Repertory UI', title: 'Repertory UI',
...extra, ...extra,
webPreferences: { webPreferences: {
nodeIntegration: true,
webSecurity: !process.env.ELECTRON_START_URL webSecurity: !process.env.ELECTRON_START_URL
} }
}); });
if (platform === 'linux') {
if ((os.platform() === 'darwin') && launchHidden) { mainWindow.setMenuBarVisibility(false);
} else {
mainWindow.removeMenu();
}
if ((platform === 'darwin') && launchHidden) {
app.dock.hide(); app.dock.hide();
} }
@@ -121,8 +137,8 @@ const createWindow = () => {
MountsIPC.unmountAllDrives(); MountsIPC.unmountAllDrives();
}); });
const appPath = (os.platform() === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) : const appPath = (platform === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) :
(os.platform() === 'darwin') ? path.resolve(path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')) : (platform === 'darwin') ? path.resolve(path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')) :
process.env.APPIMAGE; process.env.APPIMAGE;
const autoLauncher = new AutoLaunch({ const autoLauncher = new AutoLaunch({
@@ -166,19 +182,19 @@ const createWindow = () => {
} }
]); ]);
const image = nativeImage.createFromPath(path.join(__dirname, (os.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
.isEnabled() .isEnabled()
.then((enabled) => { .then((enabled) => {
trayContextMenu.items[1].checked = enabled; trayContextMenu.items[1].checked = enabled;
mainWindowTray.setToolTip('Repertory UI'); mainWindowTray.setToolTip('Repertory UI');
mainWindowTray.setContextMenu(trayContextMenu) mainWindowTray.setContextMenu(trayContextMenu)
}) })
.catch(() => { .catch(() => {
closeApplication(); closeApplication();
}); });
mainWindow.loadURL(startUrl); mainWindow.loadURL(startUrl);
}; };
@@ -188,7 +204,7 @@ const getMainWindow = () => {
}; };
const loadUiSettings = () => { const loadUiSettings = () => {
const settingFile = path.join(helpers.resolvePath(Constants.DATA_LOCATIONS[os.platform()]), 'ui.json'); const settingFile = path.join(helpers.resolvePath(Constants.DATA_LOCATIONS[platform]), 'ui.json');
try { try {
if (fs.statSync(settingFile).isFile()) { if (fs.statSync(settingFile).isFile()) {
const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8')); const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8'));
@@ -217,9 +233,9 @@ const setIsInstalling = installing => {
const setTrayImage = driveInUse => { const setTrayImage = driveInUse => {
let image; let image;
if (driveInUse) { if (driveInUse) {
image = nativeImage.createFromPath(path.join(__dirname, os.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, os.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);
}; };
@@ -227,8 +243,10 @@ const setTrayImage = driveInUse => {
const setWindowVisibility = show => { const setWindowVisibility = show => {
if (show) { if (show) {
mainWindow.show(); mainWindow.show();
if (os.platform() === 'darwin') { if (platform === 'darwin') {
app.dock.show(); app.dock.show();
} else if (platform === 'linux') {
mainWindow.setSize(dimensions.width, dimensions.height);
} }
if (mainWindow.isMinimized()) { if (mainWindow.isMinimized()) {
@@ -237,7 +255,7 @@ const setWindowVisibility = show => {
mainWindow.focus(); mainWindow.focus();
} else { } else {
mainWindow.hide(); mainWindow.hide();
if (os.platform() === 'darwin') { if (platform === 'darwin') {
app.dock.hide(); app.dock.hide();
} }
} }
@@ -260,9 +278,6 @@ const standardIPCReply = (event, channel, data, error) => {
} }
}; };
app.on('before-quit', function () { app.on('before-quit', function () {
isQuiting = true; isQuiting = true;
}); });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 262 B

View File

@@ -1,220 +1,246 @@
{ {
"Locations": { "Locations": {
"arch": { "arch": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"centos7": { "centos7": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"darwin": { "darwin": {
"1.0.8": { "1.1.2": {
"sha256": "fa978eeb6f5456c1fcea902cafb039eeda6d13f93770150564a0253341cb3d63", "sha256": "0c9c4de2006ecc4ecc6166af979cc5e0b66212472d21c542d4198013e354e275",
"sig": "CfzV0Jl/Saq6Yqtwz21Qm5F7C/geGtmotifZrUcSFpGTaniYbtWtwJSap1tAep1EzL43l4wtxmpDM9EJ3qkquD6JMQjKK89twYotzMoWneZyyEB7obhpi/M3mfih8fYrPCgxaUkVm4DZ4DNbZn2GwjMmdCV698OnUfg2PKhVH4LnjqekqOEsU02B/cvlym2zPLLoEhjJoL8+s2ICIV3WBVDHpcF4ZF3lnI8HgfjjpvhQ+V8LQCk+LQTzIwwBDj6C1sH5V9om7ZUyZZEQk8ALcvo6EJHhhOoQ6extou5NIfBSkDir9IJW8hnTVykgYwoFXSUSAf7tvLn/fx+xpvp27hh+MQqA7KPhVyUD0I2qobm87KV9RBUuW0eqC/ijkcfacyhBnTEikSrHbew3cy69KtBTs4xaicIErQogJXcRfn0bQsVYxk1H3lRyoZnnaFliLwBw/8NQnrvjZqMiqlVXijZj6w0gWZjfrJ/s7UZ0K+yofVAyp3Qzf8JsdlUeCS+lqsv65ANWOBrUeF2Tz2neIWWISnVADmBMeacp/Xuz/bwuGLc4FnMWyhIBjBGwYLBTiaq0nk0oC6wn7a+LZoib8WtU9CAJ4v82k0e8YgiwBDPZPfU03wWWVJoRC2yUu89/6x8AQaClwNh364XWmF7bgbCyjThjC70iz2Feu0ORkj4D0vjClYnP1Kz5HOAvzCRlJTtwVnB9xrEBtV45ONaw6wRwVPkADLg/624FTHzwpi7BTTV2xCAn3kx2JvSH7n9ISySu8ZG6Z2pZ5IBs7+7jWQKmtbC8b6wB0VpYwoCcLjGIduaYtx2YKv0FvjMxOeMmrfu3X33Ab9KL/T4meu5LhqcW01JDP2MlzZ8UYebykd+0KI2mts+hGHNhCT3/d7fEh/ccmaJQ1sNl8VoKos1DlwUL+U8t7PVUNj6e4SXZK+Na9pPLI3pMPKvaoPZimbXRKShF+mYUGlLEcQLcjFTTmOnFSkIP39pa98K7mtTwy50dgcqroTm9rAoBGLcicjdlOi76XcSikYrYrBupx2LtAiWUuXrD0Zm9lQ/Lfwp3smUaC6aFNmnyxPmDJ1XscbQr3m/5oxfNopBNliZCvlGLUc/HGUpy0XcEZymPA77AZog4cHcjgAi9tsdnPHfxMmFTniThDD/SgVjTolzSu4Sm/RY2K2hCJ8qFeSuvbhmgPPWOBbNMOfsv9Nswa4Rd57uEqxxSrpAo1dzxdJPklrDsCxeexVz1/6xW3Qj+I12abMacCCa8oX+PLfpdT6hDqBSlzT3KbVz6ApWNRheM/z8kwpDCDHKGIKPZ5OjRm0SRn/SlogRCUHZ93TSOClIPRr37TDYDpvzeoSjAxnwQ2jTxuIs=", "sig": "CFzIk/7FJbhem38KzK1N37Z5g55wNeyvWZDC4rYElLxC6VEA2f4ZQym9sy47TDKamli42/IsiBSkm+1rE8Isg9AxdkYxv0TBbpBxm0W+vVaiHSHfrp4qQ3ciaxgPUiph6svAKZSIfw5pGtCshEaUlLBSMGGY4+Hi9aoH6yZojyFfhzKQP1AZbCVE5ZB7EStgg9LxzJ9U3dQZXon5G5LiZTRILPJ6XUiXKNlMzZ/DLCioMOQGnGqeDv2CDxXAaBNPdSzEz4cwti9FBBhIOhl/XIZN4i653f3zIHTn7HjJHWCn4wnhOB3fV18dxYv3I3laDXnlOwW1NCL3EVP+wlZI7fi4z5k5BntVC0+xnvPEKWWPnoDUDah1mjDQoIvX8espi0FoglOLW6qiToq5smYu9Hptkrw6ExAyvTRBZ92ythSFgzgZ/LtmQqN7aoutDA1CDp2JFkBBLu03vch22x5JUI47v6EZKHTfLe667UXBctF+xLVw0AXQdpQIVqAV4mHKx4zwlgZhk/iflGRy5OWDwIQ7Nc+CzY+u6UOI5gqEuHa2h23BickstbLHl+Qd5vK4BZf70Z+mt5M8IRQ3EXztEVhmapRQSmOlEkvEjPaHMzly95k/LpsQxrA7mxwQd5kQSptERzFQ1dKuolUwwMTjcUZPZn5ATv1bVIMtCBmXlrRd6S2liFgatnP+HCu3LnHOlkfuXWjOqL0EuFzWlTC6646SDDMqrju+z95HYpXiGDu4EI3XhXd/67AiTQdj828MNo417FlkuuVTqsLOiKY18KMx9zYyDgWzyQZ9vXUbMwrLpYB6FWLfKxwKjyA/c+tLO52D/WzUkeX+XmJPlRSi2xsjvH/5e/z6uEvqafLPwH3BnaQEsWsrRd7hludfWej4EelxW2ec+4whMkJtIXpFrX+hnxHhs7U15QjDVbq2CARjuto+4EWo5iAO+BkK+OgxyfCEBAM9yrpqyjiQXwXi6imVj6CuOu8z+LgPBrC8bdl9pfURrmnxubZKYH2NQ06YcJs1ezeipRVMJgq4VyuMcs+0zg/wMYYdAuPVbLiVzRj9IjlMgaRoZqt5goPtjg2M7OkLemy3dcmusWakQ4ylw8Jsng00K4VlBKGTNr/HbThIZpBkCVUmVpUEKlrxGUsYaNz/TzrVTYQF3jBuk7wPXiD4pFXn49LuOpI9p+LsFCZnjxHJMBg+WSkcFpoJ0NMU2tC64ksEYrvY3BO+L2/dAUQ/2yQ0HLnnIXHSQhIieTR02l4ERFUl07fgMYtqLgqDZZ0w7jW4MZBW7Nuy78tgQ11Jfgswn/slyZei09vcrqYKjBt7Np6lqAAihOzXzE2/zK874hrcKEKatayfBsbAuJg=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/6NjT6uEl", "https://pixeldrain.com/api/file/jEWmNDRX",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_mac.dmg"
] ]
} }
}, },
"debian9": { "debian9": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"debian10": { "debian10": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"fedora28": { "fedora28": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"fedora29": { "fedora29": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"fedora30": { "fedora30": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"fedora31": {
"1.1.2": {
"sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"opensuse15": { "opensuse15": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"opensuse15.1": { "opensuse15.1": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"solus": { "solus": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"tumbleweed": { "tumbleweed": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"ubuntu18.04": { "ubuntu18.04": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"ubuntu18.10": { "ubuntu18.10": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"ubuntu19.04": { "ubuntu19.04": {
"1.0.11": { "1.1.2": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19", "sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "A1OUhRTqFPQ1I4V1/R6UAu+9+QH2VWQDnB/BdCcaZZcVy91MQDXxvE0QkKi+eVbXizL/eQDdrUMyKItVDX3W390c7f0xv5sdz/xYCP7J7ArVJoStdv82JE4NvqPH2NucWSsUx5Qpd4MNQbKuRBKzs/C4JA49htJffUTncgu06Q1IIn0DhpcQeQtivaIHTTgtf3mnoh7kIeXqKjh0SX5LtdTs6RmG6eylm02QYPTC0mtGEGoDfL7cduXSxrn/cy/o9JBPcXMUUfujBo+zuxlNNKhZezhEHJP6ZONmPNQk5eh/qdOkL3QL3et7bJfJlPx9IHwwglNyHwM3lSaYsCv8LlfdovxxX6DvtNOhA/sMx4KkC+Me7YwtLKT8zrx2twXHwCxMkGPcwLr4ODyYlg9Ru4U8cIkaFjv4ycaAeatBsOJTd1IZA8DdQQEkdCJsd77EiJrvNBWwvGWev3KQ5ZIxX68ceqhbe+KBuN9IpsvcS4yHbMPcqcRbhzDZWN/x0AyUE2o9PsL1SXZSxML/TctBAVwPRf/YeDEygxdREN7pUbGFiHuQAx0uGin1c8UGSpL0hEb6WtYVcCSR2RX/LTEJG8vwGBANZqKNeN96INepb6wMgen9JpB5ZCUpA+1H91pxrdgMttrhnLHvsvRB69tgpmlR3oIjk3Nb9M9lW9e3AY/qOBYlBwU7hAdEaUg09MWXLxaBrg3KQAlsW7I/eUckZmW1sy8ehk2shlEL4GgFBxi8vf++r/+9q2GfMhkHoNxkVqCo2tGV6zaJQyMIVS5Gi9u+gYrtCTSn20U1TJrLBoqFz6p45NP1PyomdE2zdVoDWP5M4NfTSJTc7bfRtIE835GTrf2u2BNmOQw7V7D9PmbJeBTYz6+MYoD9mEhfNQQICLm1X1vE0xiHyRYgoRfy9DYWQohJryOjwmuSXrx6EnCm3N7EsmcocU8Tmk6M/TKJqjmmuBUemoVf2dOOfM3/r7yPnMXaOA0lu4b9725CxqfehVNAsxkFckaB4+C4JJ/cdT+UVT2lEP4MRyueLh2WUEbjiN2kKyBUs9zREJ+nbNSf2QSDVJySMecBbw+hJbjbNhvmRy/THUsuVFUEoKQwnk+hT4QK8jVfV8qm+oWIsKbKqExJm3phdEY/YNN/WjxqSDo6aw8X0oBpzCkUcyB3Z9pIE1kf6Ke9V9NKVJHUioXH11oi2webBtxJQtMriIgwIms01s0SUMs/iOcq9q//S8VYQJlH8eBpmvOGeGPbkCJtmGEwExcyxAocUGr2tIjQH8ZbKMXnQuuS7HhmJiWYtZF0itvMr66CxfxpOM4A3z8GCq/4wOs3XJzD7dWdAZ1AjjIUCi1BXgotIBkGXJCXPVM=", "sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/cP4E5Aia", "https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"ubuntu19.10": {
"1.1.2": {
"sha256": "e6f8c32f6bc908da8d7a21b13a0d85365b53a24035838b3d40778a39a58395c0",
"sig": "AVtYwTJJ6w/AviQX6z4OGEls0Yv5AS2Q56t5u4TuEwCyIEgQVpy6tFxHRCrmjrrQQ34WlNmDlvrHghajZYAAf28uvIcTy9uKATPO1VgCYJvtTq2SnQF3rjXPcGQ/lCHN8bLh4w3yXVAUw36y/mKZMMV7j0OcHqcrYx1kmPuJDMtqrnHDASYYSaE071LL41dTaDjcizZfAdk8wM070R+Ea43ySR9eOIgnRKMq/mCD66/JinrLK9dWUfMC9ePhOn4u18rMJN0zjcDL6b446bd8Ojv3YdIjqQ38aR/YArOCye6bsv6tB5td+w/4ZXPpj3zcuTpaClkBagR8blfCnpeY9YS4W4BcOHYE3D8oZmQyv6DsSox+V07Jc2gZ253VHUUhcQbH0CQ+i0t3eAiNnJKKtQz5QeyFSgLB1et2dqQ9AMtjGTznYIO43xmsj02rtvEJR1up1OgYsSEpVfdABm6Ohf5nPr7566ECzHMCzfprrQ9YyjodmTD1TBw3dTA8SDWFdSfj2FjfPcpW1ZHumPfb+aVmfv5j3UYvqJFkqYYC+KS0jZjPcLh/UQIswKOEqkLHMnT5bzK8PilqJ+cabJ4lu+NvRra73NKlV/B/e8ItEoZFYJbRbTy0IaToujnaH/1TpOjFgvm+Fye0+pydMj9aPDiXKl/XrXPdTbT2driN7l4chNcUtDLdvn0bs8iTN9oEZtrsGXe5dP6Mtzp0yZCRCGxLPIwRUIaWl39hhzZ1a5pOYvSv32RJmA+GGGAn59kxJh/SU/1lOB6NKZVvKO/k14bjfMvNU6XbaIvYEN55WTtGgENhBAfYB6IyszU4y0sjASf2iifa/dzlUcXczhyhkHqZClGguoSo+H86i6At9i1rRmRfHsMLAZqW482EPbC055qodfK+v0+d4zl9hpVUaonb/9JOpYuDW9suLVhoILe0M4A5lnFNNmFbQPVVpeO7YDX9bkBeiMUyekYbQJYJ/eOxjfKWeK8g7fXJ7wGRyxgKfnXGwoYE1cfRWNoV2nlOUtv4GeoW0jcBAyqq0gM+5h/rn5/rFsNvDVACP0x/3wJoPoeKKtkNHYysgmnNaI0CbnM/KoxcNjd18KjK2Dw6o7YscZ0ZKjDpbNU9ZtI+X08XwLXQMAoLH+ipV4ugTapNXaLszp0ZZg1og0tuAIw5KExmcHMGcF3gARaUpOdTHYdN4q7Iht4j9joF6WmFUvLQal71kv7TGw8n00xpEIpp+sfeQuLUY5d/KyPMpf/D+8+RCOLQt7sc3n+28gO7doVl9VS2CT2Bkw7xXdstWdMSOd2dkXthvsLgK6san2snJYjvoy1T7aArtWqllQ1DMbTxmA/LxNn/HWnWkEi/4uCssUk=",
"urls": [
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
] ]
} }
}, },
"win32": { "win32": {
"1.0.10": { "1.1.2": {
"sha256": "62a7c36fd8657feda57134ebacac4feab65ebd577668a608770cbdf05a669055", "sha256": "2534a2aa350d4ed1786c4b1cd298ccc987fae226850a1a8bd6d54a8fac39295c",
"sig": "A3t7jyMck0qrWLNAhFUxgNns/bEphbx3DzXcTnAc0g0z1l7JWBCk4MxNyovJCCGc5NmmAgK+U8KKbdGizqPaZrjggH6Sq4BjRSD/FGrX7c59G/rj1jD1y+A5aN3M77tz+2z+XVyUvtqRYN1Jh+JoBZOnHC4wywnOiu9Q942Ky4Ecn2wiNcyquHQailqR/rAVNVQ1qAWq5b54nySZs4ZuSF0uvgDYobWeYnMUSlU9p9u8ojPW0teqZKngNzexUynJQfZoBE0f4O5DUUDU4FDaWn3AcXdjnTLthPT2nJ8EXVCguod9MD9pjBfbr/UYUrSywG5bK01G3ZJLXs6dxb9xb2a9EoXPP91SDNZ3gAq+J+qKXUkN5kFCkC1Z+oyIClx08HQ0Pu24MCCSm4xYz1OAQygqCXCncmJEwvT+1qDPp8u8dobWO9lH+C4rRTUP/NHs78vCIQ0kR1r2k1q/1Xi1RalJ2N5V1nwPBRBH4iB/mQ5Z0CIYb/Wm8I5jexENWVEOWP1kTXCq9SGBDPuJYWIwrOz5UkgkZ0fQlOQaxI2ZWSTd5cUSQsljZoZ+T4pU2gMNR7T+GOjyc/hndx0w7D3gtzRy2/DVvMUmAnADCeIHfIivOtb9V1Uzu6Op84QKOv5rTe791QLplqcg620o6qIYr7lRA3tbSKrgR78XVGQA8odv79VtFh3PAGRkQ/nTkaucPBL9KmiB4KDctzLr49ky4d7xMOv68S72U69LBM886nA7vlQdEwIcW1TLkLj1n2DJADARu0Uub0M1O4PnQp/4iJic/dfWm0DIwvLltYNJrYT4G7m0KilzqcpPxhaUIkgDarcwWhisCAX9TvP9DhYQbjf0FDsp4KqhRvfn37QDI01DYRY80GSllabHf5sT0AxCgEGAPXC0BP+7OM8hjO/XXTExv4eXPlFoDI1+qLMRH+4GgnTCBks3Axe5rPhWzJZqqSLpWCNArQFhREwJqCyvdEtSyujM1bQ0nLawRlGbUJ9CJ/yQGGV92DiT3pyTaTNpLAATA9xKGuxiTMq1PGKRZTGPnGK5udvK11Vt7B8cmPpWQyixD98R989zkleuu5b/xDWXtRZcMm0ls0IZeX8/VTrhJEtYZsZ8k6UXJJ8UQJFRoQ2zlTCNGIt/9ZcslF9sfxJ7Bi1YeDJyeCykwR0RuTNunFsFKRrnDHoXxY9JihIOXNBJI9+iELqPdWwkNWtrelKciZN4edCxcg5UYF6kEGAJsrFUlYq2zfXwlxb4VQIHACTsiPsAuMGiyG3hydcFF2UGY8MKKxOeGnvyLInaJMfmQLY4iHoT2g8jBlfkgFvyHmSdM/KsJKxLxW4ve5x5QB8KX4gBVPZZW0GJxY/Tqz0=", "sig": "Azsov3SYmN/+I3ojIAtYubfEctxD+0QgtgmeubXnjMacb1us8Lu9hFb2wVVd7RGWtsHRmxkpWAi+hUd+gSjM9ahPbm164uq2licspNWeaGJFsQ6LDT6htKz0mQAyfZB9UrKCZrfFKDMXji85GqyUlGu92NlcnXK2tVxMFuB8DHKO+uG97+YisrwvoxLgVSM/9XrjfVEJMcik4URFF1tL5IRksnqA7ccuo++BmoDOcTFvRSky5tNl/KKabEJQLS72FIJI42djBKEn/tE9wxPhBUAa/ya5crOlkD+FgAywoUScbhwcGzg1EMjtDCIDEZrsmfW91jlZmJKDxTv8DaV2MFn97NZVWGWEjU9iOYzd0j4GbNtG58M6YM6ViCJ9fCjSCGrxLS76B9Ra2YShSaJJ6RbD7JiZI86btohiQBbguhvPq16BgRUi35SQCIerASYJTjCys958fSYGd66yfyQNOcFLaTzuvG5nSpbdsakLxAqRzYx+ATYY1lETWk5OMst0pkjIxFid+xw8+/btwlfrtrGx7R7WKAfQJAL/OxvXkhiOZ3aylcdnf8tspOsjUKDGHuznt6HQvdTreUDAVZL6iVL9h+QNEeTw1AISaVqy4+dtGwFGdIxacyt5az9rpnoI9w52cARQiiUyk3I5IGyBF20ia75QXoAOZcaPiBM4yF9gicG8VKW+XFO5FBQCZiktcrNZ0lB8USILjHfRSnITqEZCOKiMRBLQVdJuwa/QxkH3tQfmVFr1FN18ZiImnLITTr2ffiFhXoQTF9NQNGtLHKLe6xvPjPjQm0ZdBKKHw7y5jkkA2mQysvD10ebackP+SLPR7hq3uBhhQsBic4pVDV1GS3UsP/etExzWCwos11MpChRSG5fAmROMiLR1DkvPUJslM4A7Rm+weo91F8+48krxNYRKeJszCWBcRUJnEd5sYI83bJ0RM6+ftl+29YW8QJpVf1tJ0D22yaTI/XgGHusdQircD4vLRKPfkXn8XEApFnLV9knujzXs5vrlQNTFFarXG+f+7E6IeXE+jneaSBXru0XgYyFCy8HRqq5rgMu1SHU5UjdWQyBM1bNGXp8WOOwuDpVbTUHg16TJ99GMbvYNjL9sJNjFcsNcId7b2gNWxo60w9O8C4+GUJrexisjQCZFzL7d/PbKCvHZp1MpliNEEIV43rniY7KUcXUmvLnMrKywj1aYVTRpLDJrtjGEGZ7OeVrxKPR/wPIpV9DD05xyOpYNyamIs5uJyBoBESXUGM+6kWhc5ZO+EJvueVU7OvuqQG8l5YQAbZvLI03jgJLTCyWxvu0zdpsSUtH560zXumuVlbCqJj2XWizP+aCQbqSnvtv1hFxJYv7a9yIgvLc=",
"urls": [ "urls": [
"https://pixeldrain.com/api/file/YCzKB0a9", "https://pixeldrain.com/api/file/TkQn25Bm",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.10_win.exe" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_win.exe"
] ]
} }
} }
}, },
"Versions": { "Versions": {
"arch": [ "arch": [
"1.0.11" "1.1.2"
], ],
"centos7": [ "centos7": [
"1.0.11" "1.1.2"
], ],
"darwin": [ "darwin": [
"1.0.8" "1.1.2"
], ],
"debian9": [ "debian9": [
"1.0.11" "1.1.2"
], ],
"debian10": [ "debian10": [
"1.0.11" "1.1.2"
], ],
"fedora28": [ "fedora28": [
"1.0.11" "1.1.2"
], ],
"fedora29": [ "fedora29": [
"1.0.11" "1.1.2"
], ],
"fedora30": [ "fedora30": [
"1.0.11" "1.1.2"
],
"fedora31": [
"1.1.2"
], ],
"linux": [ "linux": [
"unavailable" "unavailable"
], ],
"opensuse15": [ "opensuse15": [
"1.0.11" "1.1.2"
], ],
"opensuse15.1": [ "opensuse15.1": [
"1.0.11" "1.1.2"
], ],
"solus": [ "solus": [
"1.0.11" "1.1.2"
], ],
"tumbleweed": [ "tumbleweed": [
"1.0.11" "1.1.2"
], ],
"ubuntu18.04": [ "ubuntu18.04": [
"1.0.11" "1.1.2"
], ],
"ubuntu18.10": [ "ubuntu18.10": [
"1.0.11" "1.1.2"
], ],
"ubuntu19.04": [ "ubuntu19.04": [
"1.0.11" "1.1.2"
],
"ubuntu19.10": [
"1.1.2"
], ],
"unknown": [ "unknown": [
"unavailable" "unavailable"
], ],
"win32": [ "win32": [
"1.0.10" "1.1.2"
] ]
} }
} }

View File

@@ -1,18 +1,14 @@
.App { .App {
display: flex;
flex-direction: column;
margin: 0; margin: 0;
padding: 10px; padding: var(--default_spacing);
box-sizing: border-box; box-sizing: border-box;
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
background-image: url('./assets/images/background.jpg'); background-image: url('./assets/images/background2.jpg');
background-size: cover; background-size: cover;
} }
.AppContainer { .AppContainer {
display: flex;
flex-direction: column;
width: 100%; width: 100%;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
@@ -20,11 +16,11 @@
.AppHeader { .AppHeader {
height: 28px; height: 28px;
margin-bottom: 8px; margin-bottom: var(--default_spacing);
box-sizing: border-box; box-sizing: border-box;
} }
.AppContent { .AppContent {
flex: 1; height: calc(100% - 36px);
box-sizing: border-box; box-sizing: border-box;
} }

View File

@@ -10,7 +10,6 @@ import Grid from './components/UI/Grid/Grid';
import InfoDetails from './components/InfoDetails/InfoDetails'; 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 Modal from './components/UI/Modal/Modal';
import MountItems from './containers/MountItems/MountItems'; import MountItems from './containers/MountItems/MountItems';
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';
@@ -27,6 +26,8 @@ import {
loadReleases, loadReleases,
setDismissUIUpgrade setDismissUIUpgrade
} from './redux/actions/release_version_actions'; } from './redux/actions/release_version_actions';
import YesNo from './components/YesNo/YesNo';
import {createModalConditionally} from './utils';
const Constants = require('./constants'); const Constants = require('./constants');
const Scheduler = require('node-schedule'); const Scheduler = require('node-schedule');
@@ -65,11 +66,6 @@ class App extends IPCContainer {
super.componentWillUnmount(); super.componentWillUnmount();
} }
createModalConditionally = (condition, jsx, critical) => {
const modalProps = {critical: critical};
return condition ? (<Modal {...modalProps}>{jsx}</Modal>) : null;
};
getSelectedVersion = () => { getSelectedVersion = () => {
return (this.props.ReleaseVersion === -1) ? return (this.props.ReleaseVersion === -1) ?
'unavailable' : 'unavailable' :
@@ -102,6 +98,9 @@ class App extends IPCContainer {
const noConsoleSupported = this.props.LocationsLookup[selectedVersion] && const noConsoleSupported = this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].no_console_supported; this.props.LocationsLookup[selectedVersion].no_console_supported;
const remoteSupported = this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].supports_remote;
const showConfig = !missingDependencies && const showConfig = !missingDependencies &&
this.props.DisplayConfiguration && this.props.DisplayConfiguration &&
!this.props.RebootRequired && !this.props.RebootRequired &&
@@ -122,49 +121,48 @@ class App extends IPCContainer {
!this.props.DismissDependencies && !this.props.DismissDependencies &&
this.props.AllowMount; this.props.AllowMount;
const infoDisplay = this.createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true); const configDisplay = createModalConditionally(showConfig, <Configuration version={selectedVersion} remoteSupported={remoteSupported} />);
const rebootDisplay = this.createModalConditionally(this.props.RebootRequired, <Reboot />); const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo/>);
const configDisplay = this.createModalConditionally(showConfig, <Configuration version={selectedVersion} />); const dependencyDisplay = createModalConditionally(showDependencies, <DependencyList/>);
const dependencyDisplay = this.createModalConditionally(showDependencies, <DependencyList/>); const downloadDisplay = createModalConditionally(this.props.DownloadActive, <DownloadProgress/>);
const downloadDisplay = this.createModalConditionally(this.props.DownloadActive, <DownloadProgress/>); const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
const errorDisplay = this.createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true); const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true);
const upgradeDisplay = this.createModalConditionally(showUpgrade, <UpgradeUI/>); const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot />);
const selectAppPlatformDisplay = this.createModalConditionally(this.props.DisplaySelectAppPlatform, <SelectAppPlatform/>); const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, <SelectAppPlatform/>);
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI/>);
let mainContent = []; let mainContent = [];
if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) { if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
mainContent = <Loading/> mainContent = (
<Box dxStyle={{height: '100%'}}>
<Loading/>
</Box>);
} else { } else {
let key = 0; let key = 0;
mainContent.push(( mainContent.push((
<div key={'rvd_' + key++} <Box key={'md_' + key++}
style={{height: '32%'}}> dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}>
<ReleaseVersionDisplay downloadDisabled={!downloadEnabled} <ReleaseVersionDisplay downloadDisabled={!downloadEnabled}
version={selectedVersion}/> version={selectedVersion}/>
</div> </Box>
)); ));
mainContent.push(<div key={'md_' + key++}
style={{paddingTop: 'var(--default_spacing)'}}/>);
if (allowMount) { if (allowMount) {
mainContent.push(( mainContent.push((
<div key={'md_' + key++}> <Box dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}
key={'md_' + key++}>
<MountItems allowConfig={allowConfig} <MountItems allowConfig={allowConfig}
allowSiaPrime={allowSiaPrime} allowSiaPrime={allowSiaPrime}
noConsoleSupported={noConsoleSupported}/> noConsoleSupported={noConsoleSupported}
</div> remoteSupported={remoteSupported}/>
</Box>
)); ));
} }
} }
return ( return (
<div className={'App'}> <div className={'App'}>
{selectAppPlatformDisplay}
{dependencyDisplay}
{upgradeDisplay}
{configDisplay}
{infoDisplay}
{downloadDisplay}
{rebootDisplay}
{errorDisplay}
<div className={'AppContainer'}> <div className={'AppContainer'}>
<div className={'AppHeader'}> <div className={'AppHeader'}>
<Box> <Box>
@@ -187,11 +185,18 @@ class App extends IPCContainer {
</Box> </Box>
</div> </div>
<div className={'AppContent'}> <div className={'AppContent'}>
<Box dxStyle={{padding: '8px 8px 0px 8px'}}> {mainContent}
{mainContent}
</Box>
</div> </div>
</div> </div>
{selectAppPlatformDisplay}
{dependencyDisplay}
{upgradeDisplay}
{configDisplay}
{infoDisplay}
{confirmDisplay}
{downloadDisplay}
{rebootDisplay}
{errorDisplay}
</div> </div>
); );
} }
@@ -205,6 +210,7 @@ const mapStateToProps = state => {
AppReady: state.common.AppReady, AppReady: state.common.AppReady,
DismissDependencies: state.install.DismissDependencies, DismissDependencies: state.install.DismissDependencies,
DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayConfirmYesNo: state.common.DisplayConfirmYesNo,
DisplayError: state.error.DisplayError, DisplayError: state.error.DisplayError,
DisplayInfo: state.error.DisplayInfo, DisplayInfo: state.error.DisplayInfo,
DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform, DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform,

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

View File

@@ -1,10 +1,18 @@
{ {
"HostConfig": { "HostConfig": {
"AgentString": "'User-Agent' used when communicating with Sia/SiaPrime's API.", "AgentString": "'User-Agent' used when communicating with Sia/ScPrime's API.",
"ApiPassword": "Password used when communicating with Sia/SiaPrime's API.\n\nThis is not the same as your wallet's password, so please do not enter it here. Sia/SiaPrime typically auto-generate this value. It is exclusively used for API authentication purposes.", "ApiPassword": "Password used when communicating with Sia/ScPrime's API.\n\nThis is not the same as your wallet's password, so please do not enter it here. Sia/ScPrime typically auto-generate this value. It is exclusively used for API authentication purposes.",
"ApiPort": "API port used to connect to Sia/SiaPrime's daemon.", "ApiPort": "API port used to connect to Sia/ScPrime's daemon.",
"HostNameOrIp": "IP address or host name of Sia/SiaPrime daemon.", "HostNameOrIp": "IP address or host name of Sia/ScPrime daemon.",
"TimeoutMs": "Number of milliseconds to wait for Sia/SiaPrime API responses before timing out." "TimeoutMs": "Number of milliseconds to wait for Sia/ScPrime API responses before timing out."
},
"RemoteMount": {
"EnableRemoteMount": "Allow mounting this location over TCP.",
"RemoteClientPoolSize": "Number of threads to use for each unique client.",
"RemoteHostNameOrIp": "Host name or IP of host to connect to for remote mounting.",
"RemoteMaxConnections": "Maximum number of TCP connections to use when communicating with remote instances.",
"RemotePort": "TCP port used for remote mounting.",
"RemoteToken": "Encryption token used for remote mounts. This value must be the same on local and remote systems."
}, },
"Settings": { "Settings": {
"ApiAuth": "Password used to communicate with Repertory's API. Auto-generated by default.", "ApiAuth": "Password used to communicate with Repertory's API. Auto-generated by default.",
@@ -20,9 +28,9 @@
"EvictionDelayMinutes": "Number of minutes to wait after all file handles are closed before allowing file to be evicted from cache.", "EvictionDelayMinutes": "Number of minutes to wait after all file handles are closed before allowing file to be evicted from cache.",
"MaxCacheSizeBytes": "This value specifies the maximum amount of local space to consume before files are removed from cache. EnableMaxCacheSize must also be set to true for this value to take affect.", "MaxCacheSizeBytes": "This value specifies the maximum amount of local space to consume before files are removed from cache. EnableMaxCacheSize must also be set to true for this value to take affect.",
"MinimumRedundancy": "Files are elected for removal once this value has been reached. Be aware that this value cannot be set to less than 1.5x.", "MinimumRedundancy": "Files are elected for removal once this value has been reached. Be aware that this value cannot be set to less than 1.5x.",
"OnlineCheckRetrySeconds": "Number of seconds to wait for Sia/SiaPrime daemon to become available/connectable.", "OnlineCheckRetrySeconds": "Number of seconds to wait for Sia/ScPrime daemon to become available/connectable.",
"OrphanedFileRetentionDays": "Repertory attempts to keep modifications between Sia-UI and the mounted location in sync as much as possible. In the event a file is removed from Sia-UI, it will be marked as orphaned in Repertory and moved into an 'orphaned' directory within Repertory's data directory. This setting specifies the number of days this file is retained before final deletion.", "OrphanedFileRetentionDays": "Repertory attempts to keep modifications between Sia-UI and the mounted location in sync as much as possible. In the event a file is removed from Sia-UI, it will be marked as orphaned in Repertory and moved into an 'orphaned' directory within Repertory's data directory. This setting specifies the number of days this file is retained before final deletion.",
"PreferredDownloadType": "Repertory supports 3 download modes for reading files that are not cached locally: full file allocation, ring buffer mode and direct mode.\n\nFull file allocation mode pre-allocates the entire file prior to downloading. This mode is required for writes but also ensures the best performance when reading data.\n\nRing buffer mode utilizes a fixed size file buffer to enable a reasonable amount of seeking. This alleviates the need to fully allocate a file. By default, it is 512MiB. When the buffer is full, it attempts to maintain the ability to seek 50% ahead or behind the current read location without the need to re-download data from Sia/SiaPrime.\n\nDirect mode utilizes no disk space. All data is read directly from Sia/SiaPrime.\n\nPreferred download type modes are:\n\nFallback - If there isn't enough local space to allocate the full file, ring buffer mode is used. If there isn't enough local space to create the ring buffer's file, then direct mode is used.\nRingBuffer - Full file allocation is always bypassed; however, if there isn't enough space to create the ring buffer's file, then direct mode will be chosen.\nDirect - All files will be read directly from Sia/SiaPrime.", "PreferredDownloadType": "Repertory supports 3 download modes for reading files that are not cached locally: full file allocation, ring buffer mode and direct mode.\n\nFull file allocation mode pre-allocates the entire file prior to downloading. This mode is required for writes but also ensures the best performance when reading data.\n\nRing buffer mode utilizes a fixed size file buffer to enable a reasonable amount of seeking. This alleviates the need to fully allocate a file. By default, it is 512MiB. When the buffer is full, it attempts to maintain the ability to seek 50% ahead or behind the current read location without the need to re-download data from Sia/ScPrime.\n\nDirect mode utilizes no disk space. All data is read directly from Sia/ScPrime.\n\nPreferred download type modes are:\n\nFallback - If there isn't enough local space to allocate the full file, ring buffer mode is used. If there isn't enough local space to create the ring buffer's file, then direct mode is used.\nRingBuffer - Full file allocation is always bypassed; however, if there isn't enough space to create the ring buffer's file, then direct mode will be chosen.\nDirect - All files will be read directly from Sia/ScPrime.",
"ReadAheadCount": "This value specifies the number of read-ahead chunks to use when downloading a file. This is a per-open file setting and will result in the creation of an equal number of threads.", "ReadAheadCount": "This value specifies the number of read-ahead chunks to use when downloading a file. This is a per-open file setting and will result in the creation of an equal number of threads.",
"RingBufferFileSize": "The size of the ring buffer file in MiB. Default is 512. Valid values are: 64, 128, 256, 512, 1024." "RingBufferFileSize": "The size of the ring buffer file in MiB. Default is 512. Valid values are: 64, 128, 256, 512, 1024."
} }

View File

@@ -1,40 +0,0 @@
.ConfigurationItem {
margin: 0;
padding: 0;
}
input.ConfigurationItemInput {
display: block;
margin: 0;
padding: 2px;
border-radius: var(--border_radius);
background: rgba(160, 160, 160, 0.1);
border: none;
box-shadow: none;
outline: none;
color: var(--text_color);
box-sizing: border-box;
}
.ConfigurationItemSelect {
display: block;
margin: 0;
padding: 2px;
border-radius: var(--border_radius);
background: rgba(160, 160, 160, 0.1);
border: none;
box-shadow: none;
outline: none;
color: var(--text_color);
box-sizing: border-box;
}
.ConfigurationItemOption {
background: rgba(10, 10, 15, 0.8);
border-color: rgba(10, 10, 20, 0.9);
color: var(--text_color);
}
.ConfigurationInfo {
cursor: pointer;
}

View File

@@ -7,5 +7,5 @@
.ErrorDetailsContent { .ErrorDetailsContent {
max-height: 60vh; max-height: 60vh;
overflow-y: auto; overflow-y: auto;
margin-bottom: 8px; margin-bottom: var(--default_spacing);
} }

View File

@@ -19,7 +19,7 @@ const mapDispatchToProps = dispatch => {
export default connect(mapStateToProps, mapDispatchToProps)(props => { export default connect(mapStateToProps, mapDispatchToProps)(props => {
return ( return (
<Box dxDark dxStyle={{padding: '8px'}}> <Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'ErrorDetailsHeading'}>Application Error</h1> <h1 className={'ErrorDetailsHeading'}>Application Error</h1>
<div className={'ErrorDetailsContent'}> <div className={'ErrorDetailsContent'}>
<p>{props.ErrorMessage}</p> <p>{props.ErrorMessage}</p>

View File

@@ -7,5 +7,5 @@
max-height: 60vh; max-height: 60vh;
min-width: 80vw; min-width: 80vw;
overflow-y: auto; overflow-y: auto;
margin-bottom: 8px; margin-bottom: var(--default_spacing);
} }

View File

@@ -19,7 +19,7 @@ const mapDispatchToProps = dispatch => {
export default connect(mapStateToProps, mapDispatchToProps)(props => { export default connect(mapStateToProps, mapDispatchToProps)(props => {
return ( return (
<Box dxDark dxStyle={{padding: '8px'}}> <Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1> <h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1>
<div className={'InfoDetailsContent'}> <div className={'InfoDetailsContent'}>
<p style={{textAlign: 'left', whiteSpace: 'pre-line'}}>{props.InfoMessage.message}</p> <p style={{textAlign: 'left', whiteSpace: 'pre-line'}}>{props.InfoMessage.message}</p>

View File

@@ -7,5 +7,5 @@
.RebootContent { .RebootContent {
max-height: 60vh; max-height: 60vh;
overflow-y: auto; overflow-y: auto;
margin-bottom: 8px; margin-bottom: var(--default_spacing);
} }

View File

@@ -13,7 +13,7 @@ const mapDispatchToProps = dispatch => {
export default connect(null, mapDispatchToProps)(props => { export default connect(null, mapDispatchToProps)(props => {
return ( return (
<Box dxDark dxStyle={{padding: '8px'}}> <Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'RebootHeading'}>Reboot System</h1> <h1 className={'RebootHeading'}>Reboot System</h1>
<div className={'RebootContent'}> <div className={'RebootContent'}>
<p>Repertory requires a system reboot to continue.</p> <p>Repertory requires a system reboot to continue.</p>

View File

@@ -41,13 +41,14 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
}; };
const handleReleaseChanged = e => { const handleReleaseChanged = e => {
const release = parseInt(e.target.value, 10); const release = Constants.RELEASE_TYPES.indexOf(e.target.value);
const releaseVersion = props.VersionLookup[Constants.RELEASE_TYPES[release]].length - 1; const releaseVersion = props.VersionLookup[Constants.RELEASE_TYPES[release]].length - 1;
props.setActiveRelease(release, releaseVersion); props.setActiveRelease(release, releaseVersion);
}; };
const handleVersionChanged = e => { const handleVersionChanged = e => {
props.setActiveRelease(props.Release, parseInt(e.target.value, 10)); 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 text = props.InstalledVersion + ' [' + props.AppPlatform + ']';
@@ -107,7 +108,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
} }
return ( return (
<Grid> <Grid noScroll>
<Text colSpan={columns=>columns / 3} <Text colSpan={columns=>columns / 3}
rowSpan={4} rowSpan={4}
text={'Release'} text={'Release'}
@@ -119,7 +120,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
items={Constants.RELEASE_TYPES} items={Constants.RELEASE_TYPES}
row={5} row={5}
rowSpan={7} rowSpan={7}
selected={props.Release}/> selected={Constants.RELEASE_TYPES[props.Release]}/>
<Text col={dimensions => dimensions.columns / 3} <Text col={dimensions => dimensions.columns / 3}
colSpan={remain=>remain / 2} colSpan={remain=>remain / 2}
rowSpan={4} rowSpan={4}
@@ -138,7 +139,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]} items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]}
row={5} row={5}
rowSpan={7} rowSpan={7}
selected={props.ReleaseVersion}/> selected={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion]}/>
{optionsDisplay} {optionsDisplay}
</Grid> </Grid>
); );

View File

@@ -0,0 +1,76 @@
.CheckBoxOwner {
display: block;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
/* Customize the label (the container) */
label.CheckBoxLabel {
position: relative;
padding-left: 24px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default checkbox */
label.CheckBoxLabel input[type=checkbox] {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create a custom checkbox */
.CheckBoxCheckMark {
display: block;
margin: 0;
border-radius: var(--border_radius);
background-color: var(--control_background);
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
height: 20px;
width: 20px;
}
/* On mouse-over, add a grey background color */
label.CheckBoxLabel:hover input[type=checkbox] ~ .CheckBoxCheckMark {
background-color: var(--control_background_hover);
}
/* When the checkbox is checked, add a blue background */
label.CheckBoxLabel input:checked ~ .CheckBoxCheckMark {
background-color: var(--control_background);
}
/* Create the CheckBoxCheckMark/indicator (hidden when not checked) */
.CheckBoxCheckMark:after {
content: "";
position: absolute;
display: none;
}
/* Show the CheckBoxCheckMark when checked */
label.CheckBoxLabel input:checked ~ .CheckBoxCheckMark:after {
display: block;
}
/* Style the CheckBoxCheckMark/indicator */
label.CheckBoxLabel .CheckBoxCheckMark:after {
left: 6px;
top: 1px;
width: 5px;
height: 10px;
border: solid var(--heading_text_color);
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}

View File

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

View File

@@ -19,6 +19,24 @@
box-sizing: border-box; box-sizing: border-box;
} }
.DropDownSelect.Alt {
width: 100%;
height: 100%;
display: block;
margin: 0;
padding: 2px;
border-radius: var(--border_radius);
background: rgba(160, 160, 160, 0.1);
border: none;
color: var(--text_color);
box-sizing: border-box;
}
.DropDownSelect.Auto {
width: auto;
height: auto;
}
.DropDownOption { .DropDownOption {
background: rgba(10, 10, 15, 0.9); background: rgba(10, 10, 15, 0.9);
border-color: rgba(10, 10, 20, 0.9); border-color: rgba(10, 10, 20, 0.9);

View File

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

View File

@@ -2,17 +2,10 @@ import React, {Component} from 'react';
import './Grid.css'; import './Grid.css';
import GridComponent from './GridComponent/GridComponent'; import GridComponent from './GridComponent/GridComponent';
const DEFAULT_GRID_SIZE = 4;
export default class extends Component { export default class extends Component {
constructor(props) { resizeTimeout;
super(props);
window.addEventListener("resize", this.updateSizeAsync);
this.checkSizeInterval = setInterval(()=> {
this.updateSize()
}, 2000);
}
checkSizeInterval;
state = { state = {
calculated: false, calculated: false,
dimensions: { dimensions: {
@@ -21,21 +14,44 @@ export default class extends Component {
} }
}; };
calculateDimensions = (size) => { calculateDimensions = size => {
return { return {
columns: Math.floor(size.width / 4), columns: Math.floor(size.width / this.getGridSize()),
rows: Math.floor(size.height / 4) rows: Math.floor(size.height / this.getGridSize())
}; };
}; };
componentDidMount() {
window.addEventListener('resize', this.handleResize);
this.updateSizeAsync();
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
clearInterval(this.resizeTimeout);
};
getGridSize = () => {
return this.props.gridSize || DEFAULT_GRID_SIZE;
};
getGridSizePx = () => {
return this.getGridSize() + 'px ';
};
getSize = () => { getSize = () => {
const elem = this.refs.GridOwner; const elem = this.refs.GridOwner;
return { return {
height: elem ? elem.clientHeight : 0, height: elem ? elem.offsetHeight : 0,
width: elem ? elem.clientWidth : 0 width: elem ? elem.offsetWidth : 0
}; };
}; };
handleResize = () => {
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(this.updateSizeAsync, 10);
};
updateSize = () => { updateSize = () => {
const state = { const state = {
...this.state ...this.state
@@ -51,21 +67,11 @@ export default class extends Component {
}; };
updateSizeAsync = () => { updateSizeAsync = () => {
return new Promise((done) => { return new Promise(() => {
this.updateSize(); this.updateSize();
done();
}); });
}; };
componentDidMount() {
this.updateSizeAsync();
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateSizeAsync);
clearInterval(this.checkSizeInterval);
};
render() { render() {
let children = null; let children = null;
const dimensions = this.state.dimensions; const dimensions = this.state.dimensions;
@@ -95,28 +101,36 @@ export default class extends Component {
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 <GridComponent return (
row={row} <GridComponent row={row}
col={col} col={col}
rowSpan={rowSpan} rowSpan={rowSpan}
colSpan={colSpan} colSpan={colSpan}
key={'gc_' + i}>{child}</GridComponent>; key={'gc_' + i}>
{child}
</GridComponent>
);
} else { } else {
return null; return null;
} }
}) });
.filter(i => i !== null);
} }
const style = { const gridSizePx = this.getGridSizePx();
let style = {
style: { style: {
gridTemplateColumns: '4px '.repeat(dimensions.columns).trim(), gridTemplateColumns: gridSizePx.repeat(dimensions.columns),
gridTemplateRows: '4px '.repeat(dimensions.rows).trim(), gridTemplateRows: gridSizePx.repeat(dimensions.rows),
gridAutoColumns: '4px', gridAutoColumns: gridSizePx,
gridAutoRows: '4px' gridAutoRows: gridSizePx,
} }
}; };
if (this.props.noScroll) {
style['style'].overflowX = 'visible';
style['style'].overflowY = 'visible';
}
return ( return (
<div <div
ref='GridOwner' ref='GridOwner'
@@ -126,5 +140,5 @@ export default class extends Component {
</div> </div>
</div> </div>
) )
}; }
}; };

View File

@@ -8,8 +8,8 @@ export default props => {
className={'Loading'}> className={'Loading'}>
<div className={'LoadingContent'}> <div className={'LoadingContent'}>
<Loader color={'var(--heading_text_color)'} <Loader color={'var(--heading_text_color)'}
height='28px' height={28}
width='28px' width={28}
type='ThreeDots'/> type='ThreeDots'/>
</div> </div>
</div>); </div>);

View File

View File

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

View File

@@ -27,11 +27,10 @@ exports.DEV_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\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';
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/releases.json'; exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/releases_1.1.json';
exports.UI_RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory-ui/raw/' + REPERTORY_UI_BRANCH + '/releases.json'; exports.UI_RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory-ui/raw/' + REPERTORY_UI_BRANCH + '/releases.json';
exports.LINUX_DETECT_SCRIPT_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/detect_linux.sh'; exports.LINUX_DETECT_SCRIPT_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/detect_linux.sh';
@@ -39,7 +38,8 @@ exports.LINUX_DETECT_SCRIPT_URL = 'https://bitbucket.org/blockstorage/repertory/
exports.LINUX_SELECTABLE_PLATFORMS = [ exports.LINUX_SELECTABLE_PLATFORMS = [
'ubuntu18.04', 'ubuntu18.04',
'ubuntu18.10', 'ubuntu18.10',
'ubuntu19.04' 'ubuntu19.04',
'ubuntu19.10'
]; ];
exports.DATA_LOCATIONS = { exports.DATA_LOCATIONS = {
@@ -50,12 +50,12 @@ exports.DATA_LOCATIONS = {
exports.PROVIDER_LIST = [ exports.PROVIDER_LIST = [
'Sia', 'Sia',
'SiaPrime' 'ScPrime'
]; ];
exports.PROVIDER_ARG = { exports.PROVIDER_ARG = {
sia: '', sia: '',
siaprime: '-sp' scprime: '-sp'
}; };
exports.RELEASE_TYPES = [ exports.RELEASE_TYPES = [
@@ -87,8 +87,8 @@ exports.IPC_Check_Mount_Location = 'check_mount_location';
exports.IPC_Delete_File = 'delete_file'; exports.IPC_Delete_File = 'delete_file';
exports.IPC_Detect_Mounts = 'detect_mounts'; exports.IPC_Detect_Mount = 'detect_mount';
exports.IPC_Detect_Mounts_Reply = 'detect_mounts_reply'; exports.IPC_Detect_Mount_Reply = 'detect_mount_reply';
exports.IPC_Download_File = 'download_file'; exports.IPC_Download_File = 'download_file';
exports.IPC_Download_File_Complete = 'download_file_complete'; exports.IPC_Download_File_Complete = 'download_file_complete';
@@ -118,6 +118,9 @@ 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_Remove_Remote_Mount = 'remove_remote_mount';
exports.IPC_Remove_Remote_Mount_Reply = 'remove_remote_mount_reply';
exports.IPC_Reboot_System = 'reboot_system'; exports.IPC_Reboot_System = 'reboot_system';
exports.IPC_Save_State = 'save_state'; exports.IPC_Save_State = 'save_state';
@@ -138,4 +141,4 @@ exports.IPC_Unmount_All_Drives = 'unmount_all';
exports.IPC_Unmount_All_Drives_Reply = 'unmount_all_reply'; exports.IPC_Unmount_All_Drives_Reply = 'unmount_all_reply';
exports.IPC_Unmount_Drive = 'unmount_drive'; exports.IPC_Unmount_Drive = 'unmount_drive';
exports.IPC_Unmount_Drive_Reply = 'unmount_drive_reply'; exports.IPC_Unmount_Drive_Reply = 'unmount_drive_reply';

View File

@@ -0,0 +1,4 @@
.AddRemoteMount {
margin: 0;
padding: 0;
}

View File

@@ -0,0 +1,117 @@
import React from 'react';
import {Component} from 'react';
import './AddRemoteMount.css';
import {connect} from 'react-redux';
import Button from '../../components/UI/Button/Button';
import Box from '../../components/UI/Box/Box';
import Text from '../../components/UI/Text/Text';
import {notifyError} from '../../redux/actions/error_actions';
import {addRemoteMount} from '../../redux/actions/mount_actions';
import {createModalConditionally} from '../../utils';
const mapStateToProps = state => {
return {
RemoteMounts: state.mounts.RemoteMounts,
};
};
const mapDispatchToProps = dispatch => {
return {
addRemoteMount: (hostNameOrIp, port, token) => dispatch(addRemoteMount(hostNameOrIp, port, token)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(class extends Component {
state = {
Display: false,
HostNameOrIp: '',
Port: 20000,
Token: '',
};
addRemoteMount = () => {
if (this.state.HostNameOrIp.length === 0) {
this.props.notifyError('Hostname or IP cannot be empty.');
} else {
const provider = 'Remote' + this.state.HostNameOrIp + ':' + this.state.Port;
if (this.props.RemoteMounts.includes(provider)) {
this.props.notifyError('Remote host already exists');
} else {
this.setState({
Display: false
}, () => {
this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token);
this.setState({
HostNameOrIp: '',
Port: 20000,
Token: '',
});
});
}
}
};
handleAddRemoteMount = () => {
this.setState({
Display: true,
});
};
render() {
const displayAdd = createModalConditionally(this.state.Display, (
<Box dxDark
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
<h1 style={{color: 'var(--text_color_error)', textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add Remote Mount</h1>
<Text text={'Hostname or IP'}
textAlign={'left'}
type={'Heading1'}/>
<input onChange={e => this.setState({HostNameOrIp: e.target.value.trim()})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.HostNameOrIp}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<Text text={'Port'}
textAlign={'left'}
type={'Heading1'}/>
<input max={65535}
min={1025}
onChange={e => this.setState({Port: e.target.value})}
className={'ConfigurationItemInput'}
type={'number'}
value={this.state.Port}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<Text text={'Remote Token'}
textAlign={'left'}
type={'Heading1'}/>
<input onChange={e => this.setState({Token: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Token}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<table cellSpacing={1}
width="100%">
<tbody>
<tr>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => this.addRemoteMount()}>OK</Button>
</td>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({Display: false})}>Cancel</Button>
</td>
</tr>
</tbody>
</table>
</Box>
));
return (
<div className={'AddRemoteMount'}>
{displayAdd}
<Button clicked={this.handleAddRemoteMount}>Add Remote Mount</Button>
</div>
);
}
});

View File

@@ -1,6 +1,6 @@
.Configuration { .Configuration {
width: 90vw; width: calc(100vw - (var(--default_spacing) * 4));
height: 90vh; height: calc(100vh - (var(--default_spacing) * 4));
padding: 4px; padding: var(--default_spacing);
margin: 0; margin: 0;
} }

View File

@@ -3,7 +3,7 @@ import './Configuration.css';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import Box from '../../components/UI/Box/Box'; import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button'; import Button from '../../components/UI/Button/Button';
import ConfigurationItem from '../../components/ConfigurationItem/ConfigurationItem'; import ConfigurationItem from './ConfigurationItem/ConfigurationItem';
import Modal from '../../components/UI/Modal/Modal'; import Modal from '../../components/UI/Modal/Modal';
import IPCContainer from '../IPCContainer/IPCContainer'; import IPCContainer from '../IPCContainer/IPCContainer';
import {displayConfiguration} from '../../redux/actions/mount_actions'; import {displayConfiguration} from '../../redux/actions/mount_actions';
@@ -20,6 +20,7 @@ class Configuration extends IPCContainer {
ObjectLookup: {}, ObjectLookup: {},
OriginalItemList: [], OriginalItemList: [],
OriginalObjectLookup: {}, OriginalObjectLookup: {},
IsRemoteMount: false,
ItemList: [], ItemList: [],
Saving: false, Saving: false,
ShowAdvanced: false, ShowAdvanced: false,
@@ -70,6 +71,7 @@ class Configuration extends IPCContainer {
this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply); this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply);
this.sendRequest(Constants.IPC_Get_Config_Template, { this.sendRequest(Constants.IPC_Get_Config_Template, {
Provider: this.props.DisplayConfiguration, Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
Version: this.props.version, Version: this.props.version,
}); });
} }
@@ -86,8 +88,12 @@ class Configuration extends IPCContainer {
.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,
label: key, label: key,
value: config[key], remote: template[key] ? template[key].remote : false,
value: (template[key] && (template[key].type === 'object')) ?
config[key] :
config[key].toString(),
}; };
}) })
.filter(i=> { .filter(i=> {
@@ -98,7 +104,6 @@ class Configuration extends IPCContainer {
} }
return ret; return ret;
}); });
return { return {
ObjectList: objectList, ObjectList: objectList,
ItemList: itemList, ItemList: itemList,
@@ -140,9 +145,20 @@ class Configuration extends IPCContainer {
const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template); const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template);
objectLookup[obj.label] = list2.ItemList; objectLookup[obj.label] = list2.ItemList;
} }
const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
const isRemoteMount = this.props.remoteSupported &&
JSON.parse(objectLookup['RemoteMount'].find(s => s.label === 'IsRemoteMount').value);
if (isRemoteMount) {
for (const obj of list.ObjectList) {
if (obj.hide_remote) {
delete objectLookup[obj.label];
}
}
}
const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
this.setState({ this.setState({
IsRemoteMount: isRemoteMount,
ItemList: list.ItemList, ItemList: list.ItemList,
ObjectLookup: objectLookup, ObjectLookup: objectLookup,
OriginalItemList: itemListCopy, OriginalItemList: itemListCopy,
@@ -160,6 +176,7 @@ class Configuration extends IPCContainer {
}, ()=> { }, ()=> {
this.sendRequest(Constants.IPC_Get_Config, { this.sendRequest(Constants.IPC_Get_Config, {
Provider: this.props.DisplayConfiguration, Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
Version: this.props.version, Version: this.props.version,
}); });
}); });
@@ -202,17 +219,37 @@ class Configuration extends IPCContainer {
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,
Version: this.props.version, Version: this.props.version,
}); });
}); });
}; };
showRemoteConfigItem = (item, itemList) => {
if (item.advanced &&
item.remote &&
this.props.remoteSupported &&
(item.label !== 'IsRemoteMount')) {
const isRemoteMount = JSON.parse(itemList.find(s => s.label === 'IsRemoteMount').value);
const enableRemoteMount = !isRemoteMount &&
JSON.parse(itemList.find(s => s.label === 'EnableRemoteMount').value);
return (item.label === 'RemoteHostNameOrIp') || (item.label === 'RemoteMaxConnections') ?
isRemoteMount :
(item.label === 'RemotePort') || (item.label === 'RemoteToken') ?
isRemoteMount || enableRemoteMount :
(item.label === 'EnableRemoteMount') ?
!isRemoteMount :
enableRemoteMount;
}
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: '4px'}}> <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%'><tbody> <table width='100%'><tbody>
<tr> <tr>
@@ -228,7 +265,7 @@ class Configuration extends IPCContainer {
const configurationItems = this.state.ItemList const configurationItems = this.state.ItemList
.map((k, i) => { .map((k, i) => {
return ( return (
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ? ((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
<ConfigurationItem advanced={k.advanced} <ConfigurationItem advanced={k.advanced}
changed={e=>this.handleItemChanged(e, i)} changed={e=>this.handleItemChanged(e, i)}
grouping={'Settings'} grouping={'Settings'}
@@ -249,13 +286,14 @@ class Configuration extends IPCContainer {
{ {
this.state.ObjectLookup[key].map((k, i) => { this.state.ObjectLookup[key].map((k, i) => {
return ( return (
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ? (!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ?
<ConfigurationItem advanced={k.advanced} <ConfigurationItem advanced={k.advanced}
changed={e=>this.handleObjectItemChanged(e, key, i)} changed={e=>this.handleObjectItemChanged(e, key, i)}
grouping={key} grouping={key}
items={this.state.Template[key].template[k.label].items} items={this.state.Template[key].template[k.label].items}
key={i} key={i}
label={k.label} label={k.label}
readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))}
template={this.state.Template[key].template[k.label]} template={this.state.Template[key].template[k.label]}
value={k.value}/> : value={k.value}/> :
null) null)
@@ -269,12 +307,15 @@ class Configuration extends IPCContainer {
return ( return (
<div className={'Configuration'}> <div className={'Configuration'}>
{confirmSave} {confirmSave}
<Box dxDark dxStyle={{padding: '8px'}}> <Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<div style={{float: 'right', margin: 0, padding: 0, marginTop: '-4px', boxSizing: 'border-box', display: 'block'}}> <div style={{float: 'right', margin: 0, padding: 0, marginTop: '-4px', boxSizing: 'border-box', display: 'block'}}>
<b style={{cursor: 'pointer'}} <b style={{cursor: 'pointer'}}
onClick={this.checkSaveRequired}>X</b> onClick={this.checkSaveRequired}>X</b>
</div> </div>
<h1 style={{width: '100%', textAlign: 'center'}}>{this.props.DisplayConfiguration + ' Configuration'}</h1> <h1 style={{width: '100%', textAlign: 'center'}}>{(
this.props.DisplayRemoteConfiguration ?
this.props.DisplayConfiguration.substr(6) :
this.props.DisplayConfiguration) + ' Configuration'}</h1>
<div style={{overflowY: 'auto', height: '90%'}}> <div style={{overflowY: 'auto', height: '90%'}}>
{objectItems} {objectItems}
{(configurationItems.length > 0) ? <h1>Settings</h1> : null} {(configurationItems.length > 0) ? <h1>Settings</h1> : null}
@@ -289,13 +330,15 @@ class Configuration extends IPCContainer {
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
Platform: state.common.Platform,
} }
}; };
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)),
hideConfiguration: () => dispatch(displayConfiguration(null)), hideConfiguration: () => dispatch(displayConfiguration(null, false)),
} }
}; };

View File

@@ -0,0 +1,21 @@
.ConfigurationItem {
margin: 0;
padding: 0;
}
input.ConfigurationItemInput {
display: block;
margin: 0;
padding: 2px;
border-radius: var(--border_radius);
background: rgba(160, 160, 160, 0.1);
border: none;
box-shadow: none;
outline: none;
color: var(--text_color);
box-sizing: border-box;
}
.ConfigurationInfo {
cursor: pointer;
}

View File

@@ -1,10 +1,12 @@
import React from 'react'; import React from 'react';
import './ConfigurationItem.css'; import './ConfigurationItem.css';
import settings from '../../assets/settings'; import CheckBox from '../../../components/UI/CheckBox/CheckBox';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {notifyInfo} from '../../redux/actions/error_actions'; import {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {notifyInfo} from '../../../redux/actions/error_actions';
import settings from '../../../assets/settings';
import DropDown from '../../../components/UI/DropDown/DropDown';
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
@@ -36,13 +38,14 @@ export default connect(null, mapDispatchToProps)(props => {
let data; let data;
switch (props.template.type) { switch (props.template.type) {
case "bool": case "bool":
data = <input checked={JSON.parse(props.value)} data = <CheckBox changed={handleChanged}
onChange={e=>handleChanged(e)} checked={props.value}
type={'checkbox'}/>; disabled={props.readOnly}/>;
break; break;
case "double": case "double":
data = <input min={0.0} data = <input min={0.0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={e=>handleChanged(e)}
step={"0.01"} step={"0.01"}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
@@ -51,24 +54,18 @@ export default connect(null, mapDispatchToProps)(props => {
break; break;
case "list": case "list":
const options = props.items.map((s, i) => { data = <DropDown alt
return ( auto
<option className={'ConfigurationItemOption'} key={i} value={s}>{s}</option> changed={handleChanged}
); disabled={props.readOnly}
}); items={props.items}
selected={props.value} />;
data = (
<select onChange={e=>handleChanged(e)}
className={'ConfigurationItemSelect'}
value={props.value}>
{options}
</select>
);
break; break;
case "string": case "string":
data = <input onChange={e=>handleChanged(e)} data = <input onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
disabled={props.readOnly}
type={'text'} type={'text'}
value={props.value}/>; value={props.value}/>;
break; break;
@@ -76,6 +73,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint8": case "uint8":
data = <input max={255} data = <input max={255}
min={0} min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
@@ -85,6 +83,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint16": case "uint16":
data = <input max={65535} data = <input max={65535}
min={0} min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
@@ -94,6 +93,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint32": case "uint32":
data = <input max={4294967295} data = <input max={4294967295}
min={0} min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}
@@ -103,6 +103,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint64": case "uint64":
data = <input max={18446744073709551615} data = <input max={18446744073709551615}
min={0} min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)} onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'} className={'ConfigurationItemInput'}
type={'number'} type={'number'}

View File

@@ -1,3 +1,10 @@
.MountItem {
padding: 0;
margin: 0;
height: 56px;
overflow: visible;
}
input.MountItemInput { input.MountItemInput {
margin: 0; margin: 0;
padding: 4px; padding: 4px;

View File

@@ -1,14 +1,17 @@
import React from 'react'; import React from 'react';
import './MountItem.css'; import './MountItem.css';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import DropDown from '../UI/DropDown/DropDown'; import DropDown from '../../../components/UI/DropDown/DropDown';
import Button from '../UI/Button/Button'; import Button from '../../../components/UI/Button/Button';
import Loader from 'react-loader-spinner'; import Loader from 'react-loader-spinner';
import Text from '../UI/Text/Text'; import Text from '../../../components/UI/Text/Text';
import Grid from '../UI/Grid/Grid'; import Grid from '../../../components/UI/Grid/Grid';
import configureImage from '../../assets/images/configure.png'; import configureImage from '../../../assets/images/configure.png';
import RootElem from '../../hoc/RootElem/RootElem'; import RootElem from '../../../components/UI/RootElem/RootElem';
import {displayConfiguration, setProviderState} from '../../redux/actions/mount_actions'; import {displayConfiguration, removeRemoteMount, setProviderState} from '../../../redux/actions/mount_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 mapStateToProps = (state, ownProps) => {
return { return {
@@ -20,7 +23,8 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
displayConfiguration: provider => dispatch(displayConfiguration(provider)), displayConfiguration: (provider, remote) => dispatch(displayConfiguration(provider, remote)),
removeRemoteMount: provider => dispatch(removeRemoteMount(provider)),
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)), setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
} }
}; };
@@ -51,7 +55,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
rowSpan={6}> rowSpan={6}>
<img alt='' <img alt=''
height={'16px'} height={'16px'}
onClick={props.MState.AllowMount ? ()=>props.displayConfiguration(props.provider) : e=>{e.preventDefault();}} onClick={props.MState.AllowMount ? ()=>props.displayConfiguration(props.provider, props.remote) : e=>{e.preventDefault();}}
src={configureImage} src={configureImage}
style={{padding: 0, border: 0, margin: 0, ...pointer}} style={{padding: 0, border: 0, margin: 0, ...pointer}}
width={'16px'}/> width={'16px'}/>
@@ -63,16 +67,17 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
let inputControls = null; let inputControls = null;
if (props.Platform === 'win32') { if (props.Platform === 'win32') {
inputColumnSpan = 20; inputColumnSpan = 20;
const index = props.MState.DriveLetters.indexOf(props.PState.MountLocation);
inputControls = <DropDown changed={props.changed} inputControls = <DropDown changed={props.changed}
colSpan={inputColumnSpan} colSpan={inputColumnSpan}
disabled={!props.MState.AllowMount || props.MState.Mounted} disabled={!props.MState.AllowMount || props.MState.Mounted}
items={props.MState.DriveLetters} items={props.MState.DriveLetters}
row={secondRow} row={secondRow}
rowSpan={7} rowSpan={7}
selected={props.MState.DriveLetters.indexOf(props.PState.MountLocation)}/>; selected={index >= 0 ? props.PState.MountLocation : ''}/>;
} else { } else {
inputColumnSpan = 58; inputColumnSpan = 64;
inputControls = []; inputControls = [];
let key = 0; let key = 0;
inputControls.push(( inputControls.push((
@@ -103,12 +108,12 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
const buttonDisplay = props.MState.AllowMount ? const buttonDisplay = props.MState.AllowMount ?
(props.MState.Mounted ? 'Unmount' : 'Mount') : (props.MState.Mounted ? 'Unmount' : 'Mount') :
<Loader color={'var(--heading_text_color)'} <Loader color={'var(--heading_text_color)'}
height='19px' height={19}
type='Circles' type='Circles'
width='19px'/>; width={19}/>;
const actionsDisplay = ( const actionsDisplay = (
<Button clicked={()=>props.clicked(props.provider, !props.MState.Mounted, props.PState.MountLocation)} <Button clicked={()=>props.clicked(props.provider, props.remote, !props.MState.Mounted, props.PState.MountLocation)}
col={inputColumnSpan + 2} col={inputColumnSpan + 2}
colSpan={21} colSpan={21}
disabled={!props.MState.AllowMount} disabled={!props.MState.AllowMount}
@@ -122,9 +127,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
colSpan={28} colSpan={28}
row={secondRow} row={secondRow}
rowSpan={7}> rowSpan={7}>
<input checked={props.PState.AutoMount} <CheckBox changed={handleAutoMountChanged}
onChange={handleAutoMountChanged} checked={props.PState.AutoMount} label={'Auto-mount'}/>
type='checkbox'/>Auto-mount
</RootElem> </RootElem>
); );
@@ -133,24 +137,48 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
colSpan={24} colSpan={24}
row={secondRow} row={secondRow}
rowSpan={7}> rowSpan={7}>
<input checked={props.PState.AutoRestart} <CheckBox changed={handleAutoRestartChanged}
onChange={handleAutoRestartChanged} checked={props.PState.AutoRestart}
type='checkbox'/>Restart label={'Restart'}/>
</RootElem> </RootElem>
); );
let removeControl;
if (props.remote) {
const removeDisabled = !props.MState.AllowMount || props.MState.Mounted;
const removeStyle = {
cursor: removeDisabled ? 'no-drop' : 'pointer'
};
const handleRemoveMount = () => {
if (!removeDisabled) {
props.removeRemoteMount(props.provider);
}
};
removeControl = (
<a col={dimensions=>dimensions.columns - 6}
href={void(0)}
onClick={handleRemoveMount}
row={secondRow + 3}
style={removeStyle}>
<FontAwesomeIcon icon={faTrashAlt}/>
</a>);
}
return ( return (
<Grid> <div className={'MountItem'}>
{configButton} <Grid noScroll>
<Text {configButton}
col={configButton ? 6 : 0} <Text
rowSpan={5} col={configButton ? 6 : 0}
text={props.provider} rowSpan={5}
type={'Heading1'}/> text={props.remote ? props.provider.substr(6) : props.provider}
{inputControls} type={'Heading1'}/>
{actionsDisplay} {inputControls}
{autoMountControl} {actionsDisplay}
{autoRestartControl} {autoMountControl}
</Grid> {autoRestartControl}
{removeControl}
</Grid>
</div>
); );
}); });

View File

@@ -1,6 +1,18 @@
.MountItems { .MountItems {
padding: 0; padding: 0;
margin: 0; margin: 0;
height: 121px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden;
}
.MountItemsRemote {
padding: 0;
margin: 0;
height: 121px;
width: 100%;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: scroll;
} }

View File

@@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import AddRemoteMount from '../AddRemoteMount/AddRemoteMount';
import Box from '../../components/UI/Box/Box'; import Box from '../../components/UI/Box/Box';
import Button from '../../components/UI/Button/Button'; import Button from '../../components/UI/Button/Button';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import './MountItems.css'; import './MountItems.css';
import Modal from '../../components/UI/Modal/Modal'; import Modal from '../../components/UI/Modal/Modal';
import MountItem from '../../components/MountItem/MountItem'; import MountItem from './MountItem/MountItem';
import IPCContainer from '../IPCContainer/IPCContainer'; import IPCContainer from '../IPCContainer/IPCContainer';
import { import {
resetMountsState, resetMountsState,
@@ -21,12 +22,18 @@ const Constants = require('../../constants');
class MountItems extends IPCContainer { class MountItems extends IPCContainer {
retryIntervals = {}; retryIntervals = {};
activeDetections = [];
state = { state = {
DisplayRetry: false, DisplayRetry: false,
RetryItems: {}, RetryItems: {},
}; };
addMountsBusy = provider => {
this.props.setMountsBusy(true);
this.activeDetections.push(provider);
};
cancelRetryMount = (provider, stateCallback) => { cancelRetryMount = (provider, stateCallback) => {
clearInterval(this.retryIntervals[provider]); clearInterval(this.retryIntervals[provider]);
delete this.retryIntervals[provider]; delete this.retryIntervals[provider];
@@ -49,7 +56,7 @@ class MountItems extends IPCContainer {
}; };
componentDidMount() { componentDidMount() {
this.setRequestHandler(Constants.IPC_Detect_Mounts_Reply, this.onDetectMountsReply); this.setRequestHandler(Constants.IPC_Detect_Mount_Reply, this.onDetectMountReply);
this.setRequestHandler(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply); this.setRequestHandler(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
this.setRequestHandler(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply); this.setRequestHandler(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
this.props.resetMountsState(); this.props.resetMountsState();
@@ -64,19 +71,30 @@ class MountItems extends IPCContainer {
} }
this.props.resetMountsState(); this.props.resetMountsState();
this.activeDetections = [];
super.componentWillUnmount(); super.componentWillUnmount();
}; };
detectMount = provider => {
this.addMountsBusy(provider);
this.sendRequest(Constants.IPC_Detect_Mount, {
RemoteMounts: this.props.RemoteMounts,
Provider: provider,
Version: this.props.InstalledVersion,
});
};
detectMounts = ()=> { detectMounts = ()=> {
if (!this.state.DisplayRetry) { if (!this.state.DisplayRetry) {
this.props.setMountsBusy(true); const providerList = this.getProviderList();
this.sendRequest(Constants.IPC_Detect_Mounts, { providerList.forEach(provider => {
Version: this.props.InstalledVersion, this.detectMount(provider);
}); });
} }
}; };
displayRetryMount = (provider, mountLocation, msg) => { displayRetryMount = (provider, remote, mountLocation, msg) => {
if (!this.state.RetryItems[provider]) { if (!this.state.RetryItems[provider]) {
let retryItems = { let retryItems = {
...this.state.RetryItems ...this.state.RetryItems
@@ -103,7 +121,7 @@ class MountItems extends IPCContainer {
const retrySeconds = retryItems[provider].RetrySeconds - 1; const retrySeconds = retryItems[provider].RetrySeconds - 1;
if (retrySeconds === 0) { if (retrySeconds === 0) {
this.cancelRetryMount(provider, () => { this.cancelRetryMount(provider, () => {
this.handleMountUnMount(provider, true, mountLocation); this.handleMountUnMount(provider, remote,true, mountLocation);
}); });
} else { } else {
retryItems[provider].RetrySeconds = retrySeconds; retryItems[provider].RetrySeconds = retrySeconds;
@@ -126,28 +144,27 @@ class MountItems extends IPCContainer {
} }
}; };
handleMountLocationChanged = (provider, value) => { handleMountLocationChanged = (provider, mountLocation) => {
const location = (this.props.Platform === 'win32') ?
this.props.MountState[provider].DriveLetters[value] :
value;
const state = { const state = {
...this.props.ProviderState[provider], ...this.props.ProviderState[provider],
MountLocation: location, MountLocation: mountLocation,
}; };
this.props.setProviderState(provider, state); this.props.setProviderState(provider, state);
}; };
handleMountUnMount = (provider, mount, location) => { handleMountUnMount = (provider, remote, 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 = this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, { let result = remote ?
Provider: provider, {Valid: true, Success: true} :
Version: this.props.InstalledVersion this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
}).data; Provider: provider,
Remote: remote,
Version: this.props.InstalledVersion
}).data;
if (result.Success) { if (result.Success) {
if (result.Valid) { if (result.Valid) {
if (this.props.Platform !== 'win32') { if (this.props.Platform !== 'win32') {
@@ -162,11 +179,11 @@ class MountItems extends IPCContainer {
} else { } else {
allowAction = false; allowAction = false;
if ((result.Code === new Uint32Array([-1])[0]) || (result.Code === new Uint8Array([-1])[0])) { if ((result.Code === new Uint32Array([-1])[0]) || (result.Code === new Uint8Array([-1])[0])) {
this.displayRetryMount(provider, location, 'Failed to connect to ' + provider + ' daemon'); this.displayRetryMount(provider, remote, location, 'Failed to connect to ' + provider + ' daemon');
} else if ((result.Code === new Uint32Array([-3])[0]) || (result.Code === new Uint8Array([-3])[0])) { } else if ((result.Code === new Uint32Array([-3])[0]) || (result.Code === new Uint8Array([-3])[0])) {
this.displayRetryMount(provider, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.'); this.displayRetryMount(provider, remote, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.');
} else { } else {
this.displayRetryMount(provider, location, 'Version check failed: ' + result.Error); this.displayRetryMount(provider, remote, location, 'Version check failed: ' + result.Error);
} }
} }
} else { } else {
@@ -174,13 +191,13 @@ class MountItems extends IPCContainer {
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, location, 'Version check failed: ' + result.Error); this.displayRetryMount(provider, remote, location, 'Version check failed: ' + result.Error);
} }
} }
} }
if (allowAction) { if (allowAction) {
this.props.setMountsBusy(true); this.addMountsBusy(provider);
this.props.setAllowMount(provider, false); this.props.setAllowMount(provider, false);
if (mount) { if (mount) {
@@ -188,12 +205,14 @@ class MountItems extends IPCContainer {
Location: location, Location: location,
NoConsoleSupported: this.props.noConsoleSupported, NoConsoleSupported: this.props.noConsoleSupported,
Provider: provider, Provider: provider,
Remote: remote,
Version: this.props.InstalledVersion, Version: this.props.InstalledVersion,
}); });
} else { } else {
this.sendRequest(Constants.IPC_Unmount_Drive, { this.sendRequest(Constants.IPC_Unmount_Drive, {
Location: location, Location: location,
Provider: provider, Provider: provider,
Remote: remote,
Version: this.props.InstalledVersion, Version: this.props.InstalledVersion,
}); });
} }
@@ -201,66 +220,86 @@ class MountItems extends IPCContainer {
} }
}; };
onDetectMountsReply = (event, arg) => { getProviderList = () => {
if (!this.state.DisplayRetry && arg.data.Success) { return [
let mountsBusy = false; ...Constants.PROVIDER_LIST,
let mountStates = {}; ...this.props.RemoteMounts,
for (const provider of Constants.PROVIDER_LIST) { ];
};
hasActiveMount = () => {
for (const provider of Object.keys(this.props.MountState)) {
if (this.props.MountState[provider].Mounted)
return true;
}
return false;
};
onDetectMountReply = (event, arg) => {
const provider = arg.data.Provider;
if (!this.state.RetryItems[provider]) {
if (arg.data.Success && (!arg.data.Active || (arg.data.Location && (arg.data.Location.length > 0)))) {
const mountState = { const mountState = {
AllowMount: true, AllowMount: true,
DriveLetters: (arg.data.DriveLetters[provider]), DriveLetters: arg.data.DriveLetters,
Mounted: (arg.data.Locations[provider].length > 0), Mounted: arg.data.Active,
}; };
this.props.setMountState(provider, mountState); this.props.setMountState(provider, mountState);
mountsBusy = mountsBusy || mountState.Mounted;
mountStates[provider] = mountState; this.updateMountLocation(provider, arg.data.Location, mountState.Mounted, arg.data.DriveLetters);
this.props.setAutoMountProcessed(provider, true);
this.removeMountsBusy(provider);
} else {
this.detectMount(provider);
this.removeMountsBusy(provider);
} }
this.props.setMountsBusy(mountsBusy);
const updateMountLocation = (data, provider) => {
const providerState = this.props.ProviderState[provider];
let location = data.Locations[provider];
if (location.length === 0) {
location = (this.props.Platform === 'win32') ?
providerState.MountLocation || data.DriveLetters[provider][0] :
providerState.MountLocation;
}
if (location !== providerState.MountLocation) {
const value = (this.props.Platform === 'win32') ?
data.DriveLetters[provider].indexOf(location) :
location;
this.handleMountLocationChanged(provider, value);
}
if (!this.props.AutoMountProcessed &&
this.props.ProviderState[provider].AutoMount &&
!mountStates[provider].Mounted &&
(location.length > 0)) {
this.handleMountUnMount(provider, true, location);
}
};
for (const provider of Constants.PROVIDER_LIST) {
updateMountLocation(arg.data, provider);
}
this.props.setAutoMountProcessed(true);
} else {
this.props.notifyError(arg.data.Error);
} }
}; };
onMountDriveReply = (event, arg) => { onMountDriveReply = (event, arg) => {
this.props.setMounted(arg.data.Provider, arg.data.Success); this.props.setMounted(arg.data.Provider, arg.data.Success);
this.detectMounts(); this.detectMount(arg.data.Provider);
this.removeMountsBusy(arg.data.Provider);
}; };
onUnmountDriveReply = (event, arg) => { onUnmountDriveReply = (event, 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.Location); this.displayRetryMount(arg.data.Provider, arg.data.Remote, arg.data.Location);
} else { } else {
this.detectMounts(); this.detectMount(arg.data.Provider);
}
this.removeMountsBusy(arg.data.Provider);
};
removeMountsBusy = provider => {
const idx = this.activeDetections.indexOf(provider);
if (idx > -1) {
this.activeDetections.splice(idx, 1);
}
this.props.setMountsBusy((this.activeDetections.length > 0) || this.hasActiveMount());
};
updateMountLocation = (provider, location, mounted, driveLetters) => {
const providerState = this.props.ProviderState[provider];
if (location.length === 0) {
location = (this.props.Platform === 'win32') ?
providerState.MountLocation || driveLetters[0] :
providerState.MountLocation;
}
if (location !== providerState.MountLocation) {
const value = (this.props.Platform === 'win32') ?
driveLetters.indexOf(location) :
location;
this.handleMountLocationChanged(provider, value);
}
if (!this.props.AutoMountProcessed[provider] &&
this.props.ProviderState[provider].AutoMount &&
!mounted &&
(location.length > 0)) {
this.handleMountUnMount(provider, this.props.RemoteMounts.includes(provider),true, location);
} }
}; };
@@ -268,52 +307,83 @@ class MountItems extends IPCContainer {
let retryDisplay; let retryDisplay;
if (this.state.DisplayRetry) { if (this.state.DisplayRetry) {
let retryList = []; let retryList = [];
let retryListCount = 0; let retryCount = 0;
for (const provider in this.state.RetryItems) { for (const provider in this.state.RetryItems) {
if (this.state.RetryItems.hasOwnProperty(provider)) { if (this.state.RetryItems.hasOwnProperty(provider)) {
retryListCount++;
if (this.state.RetryItems[provider].RetryMessage) { if (this.state.RetryItems[provider].RetryMessage) {
retryList.push(<p key={'p' + retryListCount}>{this.state.RetryItems[provider].RetryMessage}</p>); retryList.push(<p key={'rl_' + retryList.length}>{this.state.RetryItems[provider].RetryMessage}</p>);
} }
retryList.push(<Button retryList.push(<Button clicked={() => this.cancelRetryMount(provider, () => this.detectMounts())}
clicked={()=>this.cancelRetryMount(provider, ()=> this.detectMounts())} key={'rl_' + retryList.length}>Cancel {provider} Remount
key={'b' + retryListCount}>Cancel {provider} Remount ({this.state.RetryItems[provider].RetrySeconds}s)</Button>); ({this.state.RetryItems[provider].RetrySeconds}s)</Button>);
if (retryListCount < Object.keys(this.state.RetryItems).length) { if (++retryCount < Object.keys(this.state.RetryItems).length) {
retryList.push(<div style={{paddingTop: '8px'}} key={'d' + retryListCount}/>); retryList.push(<div style={{paddingTop: 'var(--default_spacing)'}}
key={'rl_' + retryList.length}/>);
} }
} }
} }
retryDisplay = ( retryDisplay = (
<Modal> <Modal>
<Box dxDark dxStyle={{padding: '8px', minWidth: '70vw'}}> <Box dxDark dxStyle={{padding: 'var(--default_spacing)', minWidth: '70vw'}}>
<h1 style={{textAlign: 'center', paddingBottom: '8px', color: 'var(--text_color_error)'}}>Mount Failed</h1> <h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)', color: 'var(--text_color_error)'}}>Mount Failed</h1>
{retryList} {retryList}
</Box> </Box>
</Modal> </Modal>
) )
} }
let footerItems = [];
if (this.props.remoteSupported) {
footerItems.push(<AddRemoteMount key={'hi_' + footerItems.length}/>);
} else {
footerItems.push(<div key={'hi_' + footerItems.length}
style={{height: '27px'}}/>);
}
let items = []; let items = [];
for (const provider of Constants.PROVIDER_LIST) { for (const provider of Constants.PROVIDER_LIST) {
items.push(( items.push((
<MountItem allowConfig={this.props.allowConfig} <MountItem allowConfig={this.props.allowConfig}
allowRemove={false}
browseClicked={this.handleBrowseLocation} browseClicked={this.handleBrowseLocation}
changed={e => this.handleMountLocationChanged(provider, e.target.value)} changed={e => this.handleMountLocationChanged(provider, e.target.value)}
clicked={this.handleMountUnMount} clicked={this.handleMountUnMount}
key={'mi_' + items.length} key={'it_' + items.length}
provider={provider}/> provider={provider}/>
)); ));
if (items.length !== this.state.length) { items.push(<div key={'it_' + items.length}
items.push(<div key={'di_' + items.length} style={{paddingTop: 'var(--default_spacing)'}} />)
style={{paddingTop: '12px'}} />) }
if (this.props.remoteSupported) {
for (const provider of this.props.RemoteMounts) {
items.push((
<MountItem allowConfig={this.props.allowConfig}
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)'}}/>)
} }
items.splice(items.length - 1, 1);
} else {
items.splice(items.length - 1, 1)
} }
return ( return (
<div className={'MountItems'}> <div style={{margin: 0, padding: 0}}>
{retryDisplay} {retryDisplay}
{items} <div className={this.props.remoteSupported ? 'MountItemsRemote' : 'MountItems'}>
{items}
</div>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
{footerItems}
</div>); </div>);
} }
} }
@@ -326,6 +396,7 @@ const mapStateToProps = state => {
MountsBusy: state.mounts.MountsBusy, MountsBusy: state.mounts.MountsBusy,
Platform: state.common.Platform, Platform: state.common.Platform,
ProviderState: state.mounts.ProviderState, ProviderState: state.mounts.ProviderState,
RemoteMounts: state.mounts.RemoteMounts,
} }
}; };
@@ -334,7 +405,7 @@ const mapDispatchToProps = dispatch => {
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: processed => dispatch(setAutoMountProcessed(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)),

View File

@@ -8,7 +8,7 @@
max-height: 60vh; max-height: 60vh;
width: 255px; width: 255px;
overflow-y: auto; overflow-y: auto;
margin-bottom: 8px; margin-bottom: var(--default_spacing);
} }
.SAPActions { .SAPActions {

View File

@@ -48,21 +48,25 @@ class SelectAppPlatform extends IPCContainer {
this.grabLatestRelease(Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]) this.grabLatestRelease(Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected])
}; };
handleChanged = e => {
this.setState({
...this.state,
Selected: Constants.LINUX_SELECTABLE_PLATFORMS.indexOf(e.target.value),
});
};
render() { render() {
return ( return (
<Box dxDark dxStyle={{padding: '8px'}}> <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={e => this.setState({ <DropDown changed={this.handleChanged}
...this.state,
Selected: e.target.value
})}
disabled={this.props.InstallTestActive} disabled={this.props.InstallTestActive}
items={Constants.LINUX_SELECTABLE_PLATFORMS} items={Constants.LINUX_SELECTABLE_PLATFORMS}
selected={this.state.Selected}/> selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}/>
<Button clicked={this.handleTestClicked} <Button clicked={this.handleTestClicked}
disabled={this.props.InstallTestActive}>Test</Button> disabled={this.props.InstallTestActive}>Test</Button>
</div> </div>

View File

@@ -8,10 +8,6 @@ const spawn = require('child_process').spawn;
const Constants = require('./constants'); const Constants = require('./constants');
const RandomString = require('randomstring'); const RandomString = require('randomstring');
const _getDataDirectory = () => {
return _resolvePath(Constants.DATA_LOCATIONS[os.platform()]);
};
const _executeProcess = (command, args=[]) => { const _executeProcess = (command, args=[]) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const processOptions = { const processOptions = {
@@ -61,6 +57,26 @@ const _execProcessGetOutput = (cmd, args) => {
}); });
}; };
const _getDataDirectory = () => {
return _resolvePath(Constants.DATA_LOCATIONS[os.platform()]);
};
const _getDefaultRepertoryArgs = (provider, remote) => {
const providerLower = provider.toLowerCase();
const args = [];
if (remote) {
args.push('-rm');
args.push(provider.substr(6));
} else if (Constants.PROVIDER_ARG[providerLower] && (Constants.PROVIDER_ARG[providerLower].length > 0)) {
args.push(Constants.PROVIDER_ARG[providerLower]);
}
return args;
};
const _getRepertoryExec = version => {
return path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
};
const _resolvePath = str => { const _resolvePath = str => {
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
return str.replace(/%([^%]+)%/g, (_, n) => { return str.replace(/%([^%]+)%/g, (_, n) => {
@@ -71,7 +87,7 @@ const _resolvePath = str => {
} }
}; };
const tryParse = (j, def) => { const _tryParse = (j, def) => {
try { try {
return JSON.parse(j); return JSON.parse(j);
} catch (e) { } catch (e) {
@@ -87,12 +103,9 @@ module.exports.checkDaemonVersion = (version, provider) => {
windowsHide: true, windowsHide: true,
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = []; const args = _getDefaultRepertoryArgs(provider, false);
args.push('-cv'); args.push('-cv');
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) {
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
const process = new spawn(command, args, processOptions); const process = new spawn(command, args, processOptions);
@@ -130,41 +143,51 @@ module.exports.createSignatureFiles = (signature, publicKey) => {
}; };
}; };
module.exports.detectRepertoryMounts = version => { module.exports.detectRepertoryMounts = (version, providerList) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const processOptions = { let mountState = {};
detached: true, const defaultData = {};
shell: false, for (const provider of providerList) {
windowsHide: true, defaultData[provider] = {
}; Active: false,
Location: '',
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); PID: -1,
const args = []; };
args.push('-status'); }
const grabStatus = index => {
const process = new spawn(command, args, processOptions); if (index >= providerList.length) {
let result = ''; resolve(mountState);
} else {
process.on('error', (err) => { const provider = providerList[index];
reject(err); const processOptions = {
}); detached: true,
shell: false,
process.stdout.on('data', (d)=> { windowsHide: true,
result += d;
});
process.on('exit', () => {
let defaultData = {};
for (const provider of Constants.PROVIDER_LIST) {
defaultData[provider] = {
Active: false,
Location: '',
PID: -1,
}; };
const command = _getRepertoryExec(version);
const args = _getDefaultRepertoryArgs(provider, !Constants.PROVIDER_LIST.includes(provider));
args.push('-status');
const process = new spawn(command, args, processOptions);
let result = '';
process.on('error', (err) => {
reject(err);
});
process.stdout.on('data', (d) => {
result += d;
});
process.on('exit', () => {
mountState[provider] = _tryParse(result, defaultData)[provider];
grabStatus(++index);
});
process.unref();
} }
resolve(tryParse(result, defaultData)); };
}); grabStatus(0);
process.unref();
}); });
}; };
@@ -320,7 +343,7 @@ module.exports.executeScript = script => {
}); });
}; };
module.exports.executeMount = (version, provider, location, noConsoleSupported, exitCallback) => { module.exports.executeMount = (version, provider, remote, location, noConsoleSupported, exitCallback) => {
return new Promise((resolve) => { return new Promise((resolve) => {
const processOptions = { const processOptions = {
detached: false, detached: false,
@@ -328,11 +351,8 @@ module.exports.executeMount = (version, provider, location, noConsoleSupported,
stdio: 'ignore', stdio: 'ignore',
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = []; const args = _getDefaultRepertoryArgs(provider, remote);
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) {
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
if ((os.platform() === 'linux') || (os.platform() === 'darwin')) { if ((os.platform() === 'linux') || (os.platform() === 'darwin')) {
args.push('-o'); args.push('-o');
@@ -365,7 +385,7 @@ module.exports.executeMount = (version, provider, location, noConsoleSupported,
}); });
}; };
module.exports.getConfig = (version, provider) => { module.exports.getConfig = (version, provider, remote) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const processOptions = { const processOptions = {
detached: true, detached: true,
@@ -373,12 +393,9 @@ module.exports.getConfig = (version, provider) => {
windowsHide: true, windowsHide: true,
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = []; const args = _getDefaultRepertoryArgs(provider, remote);
args.push('-dc'); args.push('-dc');
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) {
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
const process = new spawn(command, args, processOptions); const process = new spawn(command, args, processOptions);
let result = ''; let result = '';
@@ -411,7 +428,7 @@ module.exports.getConfig = (version, provider) => {
}); });
}; };
module.exports.getConfigTemplate = (version, provider) => { module.exports.getConfigTemplate = (version, provider, remote) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const processOptions = { const processOptions = {
detached: true, detached: true,
@@ -419,12 +436,9 @@ module.exports.getConfigTemplate = (version, provider) => {
windowsHide: true, windowsHide: true,
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = []; const args = _getDefaultRepertoryArgs(provider, remote);
args.push('-gt'); args.push('-gt');
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) {
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
const process = new spawn(command, args, processOptions); const process = new spawn(command, args, processOptions);
let result = ''; let result = '';
@@ -598,7 +612,7 @@ module.exports.performWindowsUninstall = names => {
parseLine(++index); parseLine(++index);
} }
}) })
.catch(err=> { .catch(() => {
parseLine(++index); parseLine(++index);
}); });
} else { } else {
@@ -635,7 +649,7 @@ module.exports.removeDirectoryRecursively = (p) => {
module.exports.resolvePath = _resolvePath; module.exports.resolvePath = _resolvePath;
module.exports.setConfigValue = (name, value, provider, version) => { module.exports.setConfigValue = (name, value, provider, remote, version) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const processOptions = { const processOptions = {
detached: true, detached: true,
@@ -643,14 +657,11 @@ module.exports.setConfigValue = (name, value, provider, version) => {
windowsHide: true, windowsHide: true,
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = []; const args = _getDefaultRepertoryArgs(provider, remote);
args.push('-set'); args.push('-set');
args.push(name); args.push(name);
args.push(value); args.push(value);
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) {
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
const process = new spawn(command, args, processOptions); const process = new spawn(command, args, processOptions);
@@ -666,7 +677,7 @@ module.exports.setConfigValue = (name, value, provider, version) => {
}); });
}; };
module.exports.stopMountProcess = (version, provider) => { module.exports.stopMountProcess = (version, provider, remote) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const processOptions = { const processOptions = {
detached: os.platform() === 'darwin', detached: os.platform() === 'darwin',
@@ -674,11 +685,9 @@ module.exports.stopMountProcess = (version, provider) => {
windowsHide: true, windowsHide: true,
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = ['-unmount']; const args = _getDefaultRepertoryArgs(provider, remote);
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) { args.push('-unmount');
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
const process = new spawn(command, args, processOptions); const process = new spawn(command, args, processOptions);
const pid = process.pid; const pid = process.pid;
@@ -698,18 +707,16 @@ module.exports.stopMountProcess = (version, provider) => {
}); });
}; };
module.exports.stopMountProcessSync = (version, provider) => { module.exports.stopMountProcessSync = (version, provider, remote) => {
const processOptions = { const processOptions = {
detached: true, detached: true,
shell: os.platform() !== 'darwin', shell: os.platform() !== 'darwin',
windowsHide: true, windowsHide: true,
}; };
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
const args = ['-unmount']; const args = _getDefaultRepertoryArgs(provider, remote);
if (Constants.PROVIDER_ARG[provider.toLowerCase()].length > 0) { args.push('-unmount');
args.push(Constants.PROVIDER_ARG[provider.toLowerCase()]);
}
const process = new spawn(command, args, processOptions); const process = new spawn(command, args, processOptions);
process.unref(); process.unref();
@@ -717,7 +724,7 @@ module.exports.stopMountProcessSync = (version, provider) => {
module.exports.testRepertoryBinary = version => { module.exports.testRepertoryBinary = version => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); const command = _getRepertoryExec(version);
_executeProcess(command, ['-dc']) _executeProcess(command, ['-dc'])
.then(code => { .then(code => {
if (code === 0) { if (code === 0) {

View File

@@ -1,21 +1,22 @@
:root { :root {
--border_radius: 4px; --border_radius: 4px;
--control_background: rgba(125, 145, 200, .15); --control_background: rgba(105, 105, 150, 0.2);
--control_background_hover: rgba(125, 145, 200, .30); --control_background_hover: rgba(105, 105, 150, 0.4);
--control_border: 1px solid rgba(70, 70, 70, 0.9); --control_border: 1px solid rgba(80, 80, 90, 0.9);
--control_box_shadow: 1px 1px 1px black; --control_box_shadow: 2px 2px 2px black;
--control_transparent_background: rgba(30, 30, 50, 0.3); --control_transparent_background: rgba(10, 10, 16, 0.5);
--control_dark_transparent_background: rgba(10, 10, 20, 0.8); --control_dark_transparent_background: rgba(10, 10, 16, 0.7);
--text_color: rgba(200, 205, 225, 0.8); --text_color: rgba(200, 200, 240, 0.7);
--text_color_hover: rgba(200, 205, 225, 0.9); --text_color_hover: rgba(200, 200, 225, 0.7);
--text_color_error: rgba(203, 120, 120, 0.8); --text_color_error: rgba(203, 120, 120, 0.7);
--heading_text_color: rgba(146, 175, 220, 0.7); --heading_text_color: rgba(132, 160, 230, 0.7);
--heading_other_text_color: var(--heading_text_color); --heading_other_text_color: var(--heading_text_color);
--text_color_transition: color 0.3s; --text_color_transition: color 0.3s;
--default_font_size: 14px --default_font_size: 14px;
--default_spacing: 8px;
} }
* { * {
@@ -43,8 +44,8 @@ p {
padding: 0; padding: 0;
margin: 0; margin: 0;
color: var(--text_color); color: var(--text_color);
font-size: medium; font-size: var(--default_font_size);
font-weight: bold; font-weight: normal;
text-align: center; text-align: center;
} }
@@ -78,7 +79,7 @@ p {
} }
.scrollable-content, ::-webkit-scrollbar { .scrollable-content, ::-webkit-scrollbar {
width: 10px; width: 8px;
} }
.scrollable-content, ::-webkit-scrollbar * { .scrollable-content, ::-webkit-scrollbar * {
@@ -86,5 +87,5 @@ p {
} }
.scrollable-content, ::-webkit-scrollbar-thumb { .scrollable-content, ::-webkit-scrollbar-thumb {
background: rgba(90, 90, 90, 0.6) !important; background: var(--control_background_hover) !important;
} }

View File

@@ -16,19 +16,22 @@ const ipcRenderer = getIPCRenderer();
let store; let store;
if (ipcRenderer) { if (ipcRenderer) {
ipcRenderer.on(Constants.IPC_Get_Platform_Reply, (event, arg) => { ipcRenderer.once(Constants.IPC_Get_Platform_Reply, (event, platformInfo) => {
if (arg.Platform === 'linux') { if (platformInfo.Platform === 'linux') {
const root = document.documentElement; const root = document.documentElement;
root.style.setProperty('--default_font_size', '15.3px'); root.style.setProperty('--default_font_size', '15.3px');
} }
store = createAppStore(arg.Platform, arg.AppPlatform, packageJson.version); ipcRenderer.once(Constants.IPC_Get_State_Reply, (event, result) => {
ipcRenderer.on(Constants.IPC_Get_State_Reply, (event, arg) => { if (result.data) {
if (arg.data) { store = createAppStore(platformInfo, packageJson.version, result.data);
store.dispatch(setActiveRelease(arg.data.Release, arg.data.Version)); store.dispatch(setActiveRelease(result.data.Release, result.data.Version));
const providerList = [
for (const provider of Constants.PROVIDER_LIST) { ...Constants.PROVIDER_LIST,
let state = arg.data[provider] || this.props.ProviderState[provider]; ...store.getState().mounts.RemoteMounts,
];
for (const provider of providerList) {
let state = result.data[provider] || store.getState().mounts.ProviderState[provider];
if (state.AutoMount === undefined) { if (state.AutoMount === undefined) {
state['AutoMount'] = false; state['AutoMount'] = false;
} }
@@ -37,7 +40,10 @@ if (ipcRenderer) {
} }
store.dispatch(setProviderState(provider, state)); store.dispatch(setProviderState(provider, state));
} }
} else {
store = createAppStore(platformInfo, packageJson.version, {});
} }
ReactDOM.render(( ReactDOM.render((
<Provider store={store}> <Provider store={store}>
<App/> <App/>

View File

@@ -3,6 +3,26 @@ import {createAction} from 'redux-starter-kit';
import {getIPCRenderer} from '../../utils'; import {getIPCRenderer} from '../../utils';
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
let yesNoResolvers = [];
export const confirmYesNo = title => {
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 => { export const displaySelectAppPlatform = display => {
return dispatch => { return dispatch => {
@@ -14,6 +34,25 @@ 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 notifyRebootRequired = createAction('common/notifyRebootRequired'); export const notifyRebootRequired = createAction('common/notifyRebootRequired');
export const rebootSystem = () => { export const rebootSystem = () => {
@@ -25,6 +64,33 @@ export const rebootSystem = () => {
} }
}; };
export const saveState = () => {
return (dispatch, getState) => {
const state = getState();
if (state.common.AppReady) {
let currentState = {
Release: state.relver.Release,
RemoteMounts: state.mounts.RemoteMounts,
Version: state.relver.Version,
};
const providerList = [
...Constants.PROVIDER_LIST,
...state.mounts.RemoteMounts,
];
for (const provider of providerList) {
currentState[provider] = state.mounts.ProviderState[provider];
}
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Save_State, {
State: currentState
});
}
}
};
};
export const setAllowMount = createAction('common/setAllowMount'); export const setAllowMount = createAction('common/setAllowMount');
export const setApplicationReady = createAction('common/setApplicationReady'); export const setApplicationReady = createAction('common/setApplicationReady');
@@ -60,26 +126,4 @@ export const shutdownApplication = () => {
ipcRenderer.send(Constants.IPC_Shutdown); ipcRenderer.send(Constants.IPC_Shutdown);
} }
}; };
};
export const saveState = () => {
return (dispatch, getState) => {
const state = getState();
if (state.common.AppReady) {
let currentState = {
Release: state.relver.Release,
Version: state.relver.Version,
};
for (const provider of Constants.PROVIDER_LIST) {
currentState[provider] = state.mounts.ProviderState[provider];
}
if (ipcRenderer) {
ipcRenderer.send(Constants.IPC_Save_State, {
State: currentState
});
}
}
};
}; };

View File

@@ -1,8 +1,85 @@
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {createAction} from 'redux-starter-kit'; import {createAction} from 'redux-starter-kit';
import {getIPCRenderer} from '../../utils'; import {getIPCRenderer} from '../../utils';
import {
confirmYesNo,
saveState
} from './common_actions';
import {notifyError} from './error_actions';
export const displayConfiguration = createAction('mounts/displayConfiguration'); export const addRemoteMount = (hostNameOrIp, port, token) => {
return (dispatch, getState) => {
const ipcRenderer = getIPCRenderer();
const provider = 'Remote' + hostNameOrIp + ':' + port;
dispatch(addRemoteMount2(provider));
dispatch(setBusy(true));
ipcRenderer.once(Constants.IPC_Set_Config_Values_Reply, (_, arg) => {
if (arg.data.Success) {
ipcRenderer.send(Constants.IPC_Detect_Mount, {
Provider: provider,
RemoteMounts: getState().mounts.RemoteMounts,
Version: getState().relver.InstalledVersion,
});
} else {
dispatch(notifyError('Failed to set \'RemoteToken\': ' + arg.data.Error));
dispatch(setBusy(false));
}
});
ipcRenderer.send(Constants.IPC_Set_Config_Values, {
Items: [
{Name: 'RemoteMount.RemoteHostNameOrIp', Value: hostNameOrIp},
{Name: 'RemoteMount.RemoteToken', Value: token},
{Name: 'RemoteMount.RemotePort', Value: port},
{Name: 'RemoteMount.IsRemoteMount', Value: 'true'},
],
Provider: provider,
Remote: true,
Version: getState().relver.InstalledVersion,
});
};
};
export const addRemoteMount2 = createAction('mounts/addRemoteMount2');
export const DISPLAY_CONFIGURATION = 'mounts/displayConfiguration';
export const displayConfiguration = (provider, remote) => {
return {
type: DISPLAY_CONFIGURATION,
payload: {
provider,
remote,
},
};
};
export const removeRemoteMount = provider => {
return dispatch => {
dispatch(confirmYesNo('Delete [' + provider.substr(6) + ']?'))
.then(confirmed => {
if (confirmed) {
dispatch(removeRemoteMount2(provider));
}
});
};
};
const removeRemoteMount2 = provider => {
return dispatch => {
const ipcRenderer = getIPCRenderer();
ipcRenderer.once(Constants.IPC_Remove_Remote_Mount_Reply, (_, arg) => {
if (arg.data.Success) {
dispatch(removeRemoteMount3(provider));
dispatch(saveState());
}
});
ipcRenderer.send(Constants.IPC_Remove_Remote_Mount, provider.substr(6));
};
};
export const removeRemoteMount3 = createAction('mounts/removeRemoteMount3');
export const RESET_MOUNTS_STATE = 'mounts/resetMountsState'; export const RESET_MOUNTS_STATE = 'mounts/resetMountsState';
export const resetMountsState = () => { export const resetMountsState = () => {
@@ -23,7 +100,17 @@ export const setAllowMount = (provider, allow) => {
}; };
}; };
export const setAutoMountProcessed = createAction('mounts/setAutoMountProcessed'); export const SET_AUTO_MOUNT_PROCESSED = 'mounts/setAutoMountProcessed';
export const setAutoMountProcessed = (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';

View File

@@ -53,13 +53,17 @@ 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 latestVersion = versionLookup[Constants.RELEASE_TYPES[state.Release]].length - 1;
let release = state.Release; let release = state.Release;
if (release >= Constants.RELEASE_TYPES.length) {
release = state.ReleaseDefault;
}
let latestVersion = versionLookup[Constants.RELEASE_TYPES[release]].length - 1;
let version = state.Version; let version = state.Version;
if (versionLookup[Constants.RELEASE_TYPES[release]][0] === 'unavailable') { if (versionLookup[Constants.RELEASE_TYPES[release]][0] === 'unavailable') {
release = state.ReleaseDefault; release = state.ReleaseDefault;
latestVersion = version = 0; version = latestVersion = versionLookup[Constants.RELEASE_TYPES[release]].length - 1
} else if ((version === -1) || !versionLookup[Constants.RELEASE_TYPES[state.Release]][version]) { } else if ((version === -1) || !versionLookup[Constants.RELEASE_TYPES[release]][version]) {
version = latestVersion; version = latestVersion;
} }
@@ -135,10 +139,14 @@ export const notifyActiveRelease = (release, version) => {
export const setActiveRelease = (release, version) => { export const setActiveRelease = (release, version) => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(setAllowMount(false)); dispatch(setAllowMount(false));
const relVer = getState().relver; const relver = getState().relver;
const common = getState().common; const common = getState().common;
const versions = relVer.VersionLookup[Constants.RELEASE_TYPES[release]]; if (release >= Constants.RELEASE_TYPES.length) {
dispatch(setAllowDismissDependencies(versions.length > 1)); release = relver.ReleaseDefault;
version = -1;
}
const versions = relver.VersionLookup[Constants.RELEASE_TYPES[release]];
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) {

View File

@@ -1,5 +1,6 @@
import {createReducer} from 'redux-starter-kit'; import {createReducer} from 'redux-starter-kit';
import { import {
DISPLAY_CONFIRM_YES_NO,
notifyRebootRequired, notifyRebootRequired,
setAllowMount, setAllowMount,
setApplicationReady, setApplicationReady,
@@ -7,16 +8,25 @@ import {
SET_DISPLAY_SELECT_APPPLATFORM SET_DISPLAY_SELECT_APPPLATFORM
} from '../actions/common_actions'; } from '../actions/common_actions';
export const createCommonReducer = (platform, appPlatform, version) => { export const createCommonReducer = (platformInfo, version) => {
return createReducer({ return createReducer({
AllowMount: false, AllowMount: false,
AppPlatform: appPlatform, AppPlatform: platformInfo.AppPlatform,
AppReady: false, AppReady: false,
DisplayConfirmYesNo: false,
ConfirmTitle: null,
DisplaySelectAppPlatform: false, DisplaySelectAppPlatform: false,
Platform: platform, Platform: platformInfo.Platform,
RebootRequired: false, RebootRequired: false,
Version: version, 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) => { [SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => {
return { return {
...state, ...state,

View File

@@ -1,124 +1,194 @@
import * as Constants from '../../constants'; import * as Constants from '../../constants';
import {createReducer} from 'redux-starter-kit'; import {createReducer} from 'redux-starter-kit';
import { import {
displayConfiguration, addRemoteMount2,
DISPLAY_CONFIGURATION,
removeRemoteMount3,
RESET_MOUNTS_STATE, RESET_MOUNTS_STATE,
SET_ALLOW_MOUNT, SET_ALLOW_MOUNT,
setAutoMountProcessed, SET_AUTO_MOUNT_PROCESSED,
setBusy, setBusy,
SET_MOUNT_STATE, SET_MOUNT_STATE,
SET_MOUNTED, SET_MOUNTED,
SET_PROVIDER_STATE SET_PROVIDER_STATE
} from '../actions/mount_actions'; } from '../actions/mount_actions';
const providerState = Constants.PROVIDER_LIST.map(provider=> { export const createMountReducer = state => {
return { let providerList = [
[provider]: { ...Constants.PROVIDER_LIST,
AutoMount: false, ...(state.RemoteMounts||[]),
AutoRestart: false, ];
MountLocation: '', const providerState = providerList.map(provider=> {
return {
[provider]: {
AutoMount: false,
AutoRestart: false,
MountLocation: '',
}
} }
} }).reduce((map, obj) => {
}).reduce((map, obj) => { return {
return { ...map,
...map, ...obj
...obj }
} });
});
const mountState = Constants.PROVIDER_LIST.map(provider => { const mountState = providerList.map(provider => {
return { return {
[provider]: { [provider]: {
AllowMount: false, AllowMount: false,
DriveLetters: [], DriveLetters: [],
Mounted: false, Mounted: false,
}
} }
} }).reduce((map, obj) => {
}).reduce((map, obj) => { return {
return { ...map,
...map, ...obj
...obj }
} });
});
export const mountReducer = createReducer({ const autoMountProcessed = providerList.map(provider => {
AutoMountProcessed: false,
DisplayConfiguration: null,
MountsBusy: false,
MountState: mountState,
ProviderState: providerState,
}, {
[displayConfiguration]: (state, action) => {
return { return {
...state, [provider]: false,
DisplayConfiguration: action.payload
};
},
[RESET_MOUNTS_STATE]: (state, action) => {
return {
...state,
MountsBusy: false,
MountState: mountState,
} }
}, }).reduce((map, obj) => {
[setAutoMountProcessed]: (state, action) => {
return { return {
...state, ...map,
AutoMountProcessed: action.payload ...obj
}; }
}, });
[SET_ALLOW_MOUNT]: (state, action) => {
return { return createReducer({
...state, AutoMountProcessed: autoMountProcessed,
MountState: { DisplayConfiguration: null,
...state.MountState, DisplayRemoteConfiguration: false,
[action.payload.provider]: { MountsBusy: false,
...state.MountState[action.payload.provider], MountState: mountState,
AllowMount: action.payload.allow, ProviderState: providerState,
RemoteMounts: state.RemoteMounts ? state.RemoteMounts : [],
}, {
[addRemoteMount2]: (state, action) => {
let mountState = {...state.MountState};
mountState[action.payload] = {
AllowMount: false,
DriveLetters: [],
Mounted: false,
};
let providerState = {...state.ProviderState};
providerState[action.payload] = {
AutoMount: false,
AutoRestart: false,
MountLocation: '',
};
let autoMountProcessed = {...state.AutoMountProcessed};
autoMountProcessed[action.payload] = true;
return {
...state,
AutoMountProcessed: autoMountProcessed,
MountState: mountState,
ProviderState: providerState,
RemoteMounts: [...state.RemoteMounts, action.payload],
}
},
[DISPLAY_CONFIGURATION]: (state, action) => {
return {
...state,
DisplayConfiguration: action.payload.provider,
DisplayRemoteConfiguration: action.payload.remote,
};
},
[removeRemoteMount3]: (state, action) => {
let mountState = {...state.MountState};
delete mountState[action.payload];
let providerState = {...state.ProviderState};
delete providerState[action.payload];
let autoMountProcessed = {...state.AutoMountProcessed};
delete autoMountProcessed[action.payload];
const remoteMounts = state.RemoteMounts.filter(i => i !== action.payload);
return {
...state,
AutoMountProcessed: autoMountProcessed,
MountState: mountState,
ProviderState: providerState,
RemoteMounts: remoteMounts,
};
},
[RESET_MOUNTS_STATE]: (state, action) => {
return {
...state,
MountsBusy: false,
MountState: mountState,
}
},
[SET_AUTO_MOUNT_PROCESSED]: (state, action) => {
return {
...state,
AutoMountProcessed: {
...state.AutoMountProcessed,
[action.payload.provider]: action.payload.processed,
} }
} };
}; },
}, [SET_ALLOW_MOUNT]: (state, action) => {
[setBusy]: (state, action) => { return {
return { ...state,
...state, MountState: {
MountsBusy: action.payload ...state.MountState,
}; [action.payload.provider]: {
}, ...state.MountState[action.payload.provider],
[SET_MOUNT_STATE]: (state, action) => { AllowMount: action.payload.allow,
return { }
...state,
MountState: {
...state.MountState,
[action.payload.provider]: {
...state.MountState[action.payload.provider],
...action.payload.state
},
}
};
},
[SET_MOUNTED]: (state, action) => {
return {
...state,
MountState: {
...state.MountState,
[action.payload.provider]: {
...state.MountState[action.payload.provider],
Mounted: action.payload.mounted,
} }
} };
}; },
}, [setBusy]: (state, action) => {
[SET_PROVIDER_STATE]: (state, action) => { return {
return { ...state,
...state, MountsBusy: action.payload
ProviderState: { };
...state.ProviderState, },
[action.payload.provider]: { [SET_MOUNT_STATE]: (state, action) => {
...state.ProviderState[action.payload.provider], return {
...action.payload.state ...state,
}, MountState: {
} ...state.MountState,
}; [action.payload.provider]: {
} ...state.MountState[action.payload.provider],
}); ...action.payload.state
},
}
};
},
[SET_MOUNTED]: (state, action) => {
return {
...state,
MountState: {
...state.MountState,
[action.payload.provider]: {
...state.MountState[action.payload.provider],
Mounted: action.payload.mounted,
}
}
};
},
[SET_PROVIDER_STATE]: (state, action) => {
return {
...state,
ProviderState: {
...state.ProviderState,
[action.payload.provider]: {
...state.ProviderState[action.payload.provider],
...action.payload.state
},
}
};
}
});
};

View File

@@ -3,16 +3,16 @@ import {createCommonReducer} from '../reducers/common_reducer';
import {downloadReducer} from '../reducers/download_reducer'; import {downloadReducer} from '../reducers/download_reducer';
import {errorReducer} from '../reducers/error_reducer'; import {errorReducer} from '../reducers/error_reducer';
import {installReducer} from '../reducers/install_reducer'; import {installReducer} from '../reducers/install_reducer';
import {mountReducer} from '../reducers/mount_reducer'; import {createMountReducer} from '../reducers/mount_reducer';
import {releaseVersionReducer} from '../reducers/release_version_reducer'; import {releaseVersionReducer} from '../reducers/release_version_reducer';
export default function createAppStore(platform, appPlatform, version) { export default function createAppStore(platformInfo, version, state) {
const reducer = { const reducer = {
common: createCommonReducer(platform, appPlatform, version), common: createCommonReducer(platformInfo, version),
download: downloadReducer, download: downloadReducer,
error: errorReducer, error: errorReducer,
install: installReducer, install: installReducer,
mounts: mountReducer, mounts: createMountReducer(state),
relver: releaseVersionReducer, relver: releaseVersionReducer,
}; };

View File

@@ -4,7 +4,7 @@ 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) .getConfig(data.Version, data.Provider, data.Remote)
.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, {
@@ -21,7 +21,7 @@ const addListeners = (ipcMain, standardIPCReply) => {
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) .getConfigTemplate(data.Version, data.Provider, data.Remote)
.then((data) => { .then((data) => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, { standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {
Template: data, Template: data,
@@ -36,7 +36,7 @@ const addListeners = (ipcMain, standardIPCReply) => {
const setConfigValue = (i) => { const setConfigValue = (i) => {
if (i < data.Items.length) { if (i < data.Items.length) {
helpers helpers
.setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Version) .setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Remote, data.Version)
.then(() => { .then(() => {
setConfigValue(++i); setConfigValue(++i);
}) })

View File

@@ -72,7 +72,15 @@ const addListeners = (ipcMain, standardIPCReply) => {
}; };
if (data.IsWinFSP) { if (data.IsWinFSP) {
helpers helpers
.performWindowsUninstall(["WinFsp 2019.1", "WinFsp 2019.2", "WinFsp 2019.3 B1", "WinFsp 2019.3 B2"]) .performWindowsUninstall([
"WinFsp 2019.1",
"WinFsp 2019.2",
"WinFsp 2019.3 B1",
"WinFsp 2019.3 B2",
"WinFsp 2019.3 B3",
"WinFsp 2019.3 B4",
"WinFsp 2019.3 B5"
])
.then(uninstalled => { .then(uninstalled => {
if (uninstalled) { if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, { standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
@@ -99,4 +107,4 @@ const addListeners = (ipcMain, standardIPCReply) => {
module.exports = { module.exports = {
addListeners addListeners
}; };

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