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
## 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)
* 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)
## 1.0.8

View File

@@ -1,18 +1,18 @@
# 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)
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
* Sia >=1.4.1
* SiaPrime >=1.4.0
* ScPrime >=1.4.1.2
## 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.
* **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.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 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.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
* OS X 64-bit
@@ -29,8 +29,10 @@ Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions v
* Fedora 28
* Fedora 29
* Fedora 30
* Fedora 31
* Linux Mint 19
* Linux Mint 19.1
* Linux Mint 19.2
* Manjaro
* Uses `Ubuntu 18.10` binaries for compatibility
* 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.10
* Ubuntu 19.04
* Ubuntu 19.10
* Uses `Ubuntu 19.04` binaries for compatibility
* Ubuntu 19.10
## Issues/Suggestions
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
### 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.

View File

@@ -26,6 +26,8 @@ pushd "%ROOT%"
)
set OUT_FILE=repertory-ui_%APP_VER%_win.exe
call npm install
call npm run 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")

View File

@@ -19,7 +19,7 @@ if beginsWith darwin "$OSTYPE"; then
JQ_EXEC=jq-osx-amd64
SHA256_EXEC="shasum -a 256 -b"
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
BASE64_EXEC="base64 -w0"
JQ_EXEC=jq-linux64
@@ -38,6 +38,8 @@ upload_to_bitbucket() {
chmod +x "bin/${JQ_EXEC}" || exit_script "chmod +x ${JQ_EXEC} failed"
npm install
if npm run dist; then
cd dist
${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",
"version": "1.0.11",
"version": "1.1.2",
"private": true,
"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": {
"@fortawesome/fontawesome-svg-core": "^1.2.22",
"@fortawesome/free-solid-svg-icons": "^5.10.2",
"@fortawesome/react-fontawesome": "^0.1.4",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-fontawesome": "^0.1.7",
"auto-launch": "^5.0.5",
"axios": "^0.18.1",
"electron-debug": "^2.2.0",
"axios": "^0.19.0",
"devtron": "^1.4.0",
"electron-debug": "^3.0.1",
"font-awesome": "^4.7.0",
"node-schedule": "^1.3.2",
"randomstring": "^1.1.5",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-loader-spinner": "^2.3.0",
"react-redux": "^7.1.1",
"react-scripts": "2.1.8",
"react-tooltip": "^3.10.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-loader-spinner": "^3.1.4",
"react-redux": "^7.1.3",
"react-scripts": "3.2.0",
"react-tooltip": "^3.11.1",
"redux": "^4.0.4",
"redux-starter-kit": "^0.5.1",
"redux-starter-kit": "^1.0.1",
"redux-thunk": "^2.3.0",
"unzipper": "^0.9.15",
"unzipper": "^0.10.5",
"winreg": "^1.2.4"
},
"devDependencies": {
"cross-env": "^5.2.0",
"electron": "^4.2.9",
"cross-env": "^6.0.3",
"electron": "^5.0.12",
"electron-builder": "^20.44.4",
"extract-text-webpack-plugin": "^3.0.2",
"typescript": "^3.5.3",
"typescript": "^3.7.2",
"webpack-browser-plugin": "^1.0.20"
},
"scripts": {
@@ -70,8 +71,7 @@
"node_modules/**/*",
"src/helpers.js",
"src/renderer/**/*",
"public/detect_linux.sh",
"public/install_linux.sh"
"public/detect_linux.sh"
],
"linux": {
"category": "Utility",
@@ -79,9 +79,9 @@
},
"mac": {
"category": "public.app-category.utilities",
"icon": "./build/icon_color.icns",
"target": "dmg",
"darkModeSupport": true
"darkModeSupport": true,
"icon": "./build/icon.icns"
},
"win": {
"icon": "./build/icon.ico",

View File

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

View File

@@ -14,7 +14,12 @@ const helpers = require('../src/helpers');
const os = require('os');
const path = require('path');
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) {
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 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 isQuiting = false;
let isInstalling = false;
@@ -70,28 +82,32 @@ const createWindow = () => {
loadUiSettings();
let extra = {};
if (os.platform() === 'linux') {
if (platform === 'linux') {
extra = {
icon: path.join(__dirname, '../', 'icon.png'),
icon: path.join(__dirname, '../build/', 'logo.png'),
}
}
// Create the browser window.
const height = (process.env.ELECTRON_START_URL || (os.platform() === 'darwin') ? 294 : 274) - ((os.platform() === 'win32') ? 0 : 20);
mainWindow = new BrowserWindow({
width: 428 + ((os.platform() === 'win32') ? 0 : (os.platform() === 'darwin') ? 150 : 160),
height: height,
height: dimensions.height,
width: dimensions.width,
fullscreen: false,
resizable: false,
show: !launchHidden,
title: 'Repertory UI',
...extra,
webPreferences: {
nodeIntegration: true,
webSecurity: !process.env.ELECTRON_START_URL
}
});
if ((os.platform() === 'darwin') && launchHidden) {
if (platform === 'linux') {
mainWindow.setMenuBarVisibility(false);
} else {
mainWindow.removeMenu();
}
if ((platform === 'darwin') && launchHidden) {
app.dock.hide();
}
@@ -121,8 +137,8 @@ const createWindow = () => {
MountsIPC.unmountAllDrives();
});
const appPath = (os.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')) :
const appPath = (platform === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) :
(platform === 'darwin') ? path.resolve(path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')) :
process.env.APPIMAGE;
const autoLauncher = new AutoLaunch({
@@ -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);
autoLauncher
.isEnabled()
.then((enabled) => {
trayContextMenu.items[1].checked = enabled;
mainWindowTray.setToolTip('Repertory UI');
mainWindowTray.setContextMenu(trayContextMenu)
})
.catch(() => {
closeApplication();
});
.isEnabled()
.then((enabled) => {
trayContextMenu.items[1].checked = enabled;
mainWindowTray.setToolTip('Repertory UI');
mainWindowTray.setContextMenu(trayContextMenu)
})
.catch(() => {
closeApplication();
});
mainWindow.loadURL(startUrl);
};
@@ -188,7 +204,7 @@ const getMainWindow = () => {
};
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 {
if (fs.statSync(settingFile).isFile()) {
const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8'));
@@ -217,9 +233,9 @@ const setIsInstalling = installing => {
const setTrayImage = driveInUse => {
let image;
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 {
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);
};
@@ -227,8 +243,10 @@ const setTrayImage = driveInUse => {
const setWindowVisibility = show => {
if (show) {
mainWindow.show();
if (os.platform() === 'darwin') {
if (platform === 'darwin') {
app.dock.show();
} else if (platform === 'linux') {
mainWindow.setSize(dimensions.width, dimensions.height);
}
if (mainWindow.isMinimized()) {
@@ -237,7 +255,7 @@ const setWindowVisibility = show => {
mainWindow.focus();
} else {
mainWindow.hide();
if (os.platform() === 'darwin') {
if (platform === 'darwin') {
app.dock.hide();
}
}
@@ -260,9 +278,6 @@ const standardIPCReply = (event, channel, data, error) => {
}
};
app.on('before-quit', function () {
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": {
"arch": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"centos7": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"darwin": {
"1.0.8": {
"sha256": "fa978eeb6f5456c1fcea902cafb039eeda6d13f93770150564a0253341cb3d63",
"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=",
"1.1.2": {
"sha256": "0c9c4de2006ecc4ecc6166af979cc5e0b66212472d21c542d4198013e354e275",
"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": [
"https://pixeldrain.com/api/file/6NjT6uEl",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg"
"https://pixeldrain.com/api/file/jEWmNDRX",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_mac.dmg"
]
}
},
"debian9": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"debian10": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"fedora28": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"fedora29": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"fedora30": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"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": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"opensuse15.1": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"solus": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"tumbleweed": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"ubuntu18.04": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"ubuntu18.10": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage"
]
}
},
"ubuntu19.04": {
"1.0.11": {
"sha256": "616342ec7a2fdd38dde36a24e650e7763a6150d27e67527a2ad76e55390a6d19",
"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=",
"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/cP4E5Aia",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.11_linux_x86_64.AppImage"
"https://pixeldrain.com/api/file/5i1mA1gb",
"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": {
"1.0.10": {
"sha256": "62a7c36fd8657feda57134ebacac4feab65ebd577668a608770cbdf05a669055",
"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=",
"1.1.2": {
"sha256": "2534a2aa350d4ed1786c4b1cd298ccc987fae226850a1a8bd6d54a8fac39295c",
"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": [
"https://pixeldrain.com/api/file/YCzKB0a9",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.10_win.exe"
"https://pixeldrain.com/api/file/TkQn25Bm",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_win.exe"
]
}
}
},
"Versions": {
"arch": [
"1.0.11"
"1.1.2"
],
"centos7": [
"1.0.11"
"1.1.2"
],
"darwin": [
"1.0.8"
"1.1.2"
],
"debian9": [
"1.0.11"
"1.1.2"
],
"debian10": [
"1.0.11"
"1.1.2"
],
"fedora28": [
"1.0.11"
"1.1.2"
],
"fedora29": [
"1.0.11"
"1.1.2"
],
"fedora30": [
"1.0.11"
"1.1.2"
],
"fedora31": [
"1.1.2"
],
"linux": [
"unavailable"
],
"opensuse15": [
"1.0.11"
"1.1.2"
],
"opensuse15.1": [
"1.0.11"
"1.1.2"
],
"solus": [
"1.0.11"
"1.1.2"
],
"tumbleweed": [
"1.0.11"
"1.1.2"
],
"ubuntu18.04": [
"1.0.11"
"1.1.2"
],
"ubuntu18.10": [
"1.0.11"
"1.1.2"
],
"ubuntu19.04": [
"1.0.11"
"1.1.2"
],
"ubuntu19.10": [
"1.1.2"
],
"unknown": [
"unavailable"
],
"win32": [
"1.0.10"
"1.1.2"
]
}
}

View File

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

View File

@@ -10,7 +10,6 @@ import Grid from './components/UI/Grid/Grid';
import InfoDetails from './components/InfoDetails/InfoDetails';
import IPCContainer from './containers/IPCContainer/IPCContainer';
import Loading from './components/UI/Loading/Loading';
import Modal from './components/UI/Modal/Modal';
import MountItems from './containers/MountItems/MountItems';
import {notifyError} from './redux/actions/error_actions';
import Reboot from './components/Reboot/Reboot';
@@ -27,6 +26,8 @@ import {
loadReleases,
setDismissUIUpgrade
} from './redux/actions/release_version_actions';
import YesNo from './components/YesNo/YesNo';
import {createModalConditionally} from './utils';
const Constants = require('./constants');
const Scheduler = require('node-schedule');
@@ -65,11 +66,6 @@ class App extends IPCContainer {
super.componentWillUnmount();
}
createModalConditionally = (condition, jsx, critical) => {
const modalProps = {critical: critical};
return condition ? (<Modal {...modalProps}>{jsx}</Modal>) : null;
};
getSelectedVersion = () => {
return (this.props.ReleaseVersion === -1) ?
'unavailable' :
@@ -102,6 +98,9 @@ class App extends IPCContainer {
const noConsoleSupported = this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].no_console_supported;
const remoteSupported = this.props.LocationsLookup[selectedVersion] &&
this.props.LocationsLookup[selectedVersion].supports_remote;
const showConfig = !missingDependencies &&
this.props.DisplayConfiguration &&
!this.props.RebootRequired &&
@@ -122,49 +121,48 @@ class App extends IPCContainer {
!this.props.DismissDependencies &&
this.props.AllowMount;
const infoDisplay = this.createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true);
const rebootDisplay = this.createModalConditionally(this.props.RebootRequired, <Reboot />);
const configDisplay = this.createModalConditionally(showConfig, <Configuration version={selectedVersion} />);
const dependencyDisplay = this.createModalConditionally(showDependencies, <DependencyList/>);
const downloadDisplay = this.createModalConditionally(this.props.DownloadActive, <DownloadProgress/>);
const errorDisplay = this.createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
const upgradeDisplay = this.createModalConditionally(showUpgrade, <UpgradeUI/>);
const selectAppPlatformDisplay = this.createModalConditionally(this.props.DisplaySelectAppPlatform, <SelectAppPlatform/>);
const configDisplay = createModalConditionally(showConfig, <Configuration version={selectedVersion} remoteSupported={remoteSupported} />);
const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo/>);
const dependencyDisplay = createModalConditionally(showDependencies, <DependencyList/>);
const downloadDisplay = createModalConditionally(this.props.DownloadActive, <DownloadProgress/>);
const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true);
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot />);
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, <SelectAppPlatform/>);
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI/>);
let mainContent = [];
if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
mainContent = <Loading/>
mainContent = (
<Box dxStyle={{height: '100%'}}>
<Loading/>
</Box>);
} else {
let key = 0;
mainContent.push((
<div key={'rvd_' + key++}
style={{height: '32%'}}>
<Box key={'md_' + key++}
dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}>
<ReleaseVersionDisplay downloadDisabled={!downloadEnabled}
version={selectedVersion}/>
</div>
</Box>
));
mainContent.push(<div key={'md_' + key++}
style={{paddingTop: 'var(--default_spacing)'}}/>);
if (allowMount) {
mainContent.push((
<div key={'md_' + key++}>
<Box dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}
key={'md_' + key++}>
<MountItems allowConfig={allowConfig}
allowSiaPrime={allowSiaPrime}
noConsoleSupported={noConsoleSupported}/>
</div>
noConsoleSupported={noConsoleSupported}
remoteSupported={remoteSupported}/>
</Box>
));
}
}
return (
<div className={'App'}>
{selectAppPlatformDisplay}
{dependencyDisplay}
{upgradeDisplay}
{configDisplay}
{infoDisplay}
{downloadDisplay}
{rebootDisplay}
{errorDisplay}
<div className={'AppContainer'}>
<div className={'AppHeader'}>
<Box>
@@ -187,11 +185,18 @@ class App extends IPCContainer {
</Box>
</div>
<div className={'AppContent'}>
<Box dxStyle={{padding: '8px 8px 0px 8px'}}>
{mainContent}
</Box>
{mainContent}
</div>
</div>
{selectAppPlatformDisplay}
{dependencyDisplay}
{upgradeDisplay}
{configDisplay}
{infoDisplay}
{confirmDisplay}
{downloadDisplay}
{rebootDisplay}
{errorDisplay}
</div>
);
}
@@ -205,6 +210,7 @@ const mapStateToProps = state => {
AppReady: state.common.AppReady,
DismissDependencies: state.install.DismissDependencies,
DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayConfirmYesNo: state.common.DisplayConfirmYesNo,
DisplayError: state.error.DisplayError,
DisplayInfo: state.error.DisplayInfo,
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": {
"AgentString": "'User-Agent' used when communicating with Sia/SiaPrime'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.",
"ApiPort": "API port used to connect to Sia/SiaPrime's daemon.",
"HostNameOrIp": "IP address or host name of Sia/SiaPrime daemon.",
"TimeoutMs": "Number of milliseconds to wait for Sia/SiaPrime API responses before timing out."
"AgentString": "'User-Agent' used when communicating with Sia/ScPrime's API.",
"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/ScPrime's daemon.",
"HostNameOrIp": "IP address or host name of Sia/ScPrime daemon.",
"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": {
"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.",
"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.",
"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.",
"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.",
"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 {
max-height: 60vh;
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 => {
return (
<Box dxDark dxStyle={{padding: '8px'}}>
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'ErrorDetailsHeading'}>Application Error</h1>
<div className={'ErrorDetailsContent'}>
<p>{props.ErrorMessage}</p>

View File

@@ -7,5 +7,5 @@
max-height: 60vh;
min-width: 80vw;
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 => {
return (
<Box dxDark dxStyle={{padding: '8px'}}>
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1>
<div className={'InfoDetailsContent'}>
<p style={{textAlign: 'left', whiteSpace: 'pre-line'}}>{props.InfoMessage.message}</p>

View File

@@ -7,5 +7,5 @@
.RebootContent {
max-height: 60vh;
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 => {
return (
<Box dxDark dxStyle={{padding: '8px'}}>
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'RebootHeading'}>Reboot System</h1>
<div className={'RebootContent'}>
<p>Repertory requires a system reboot to continue.</p>

View File

@@ -41,13 +41,14 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
};
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;
props.setActiveRelease(release, releaseVersion);
};
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 + ']';
@@ -107,7 +108,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
}
return (
<Grid>
<Grid noScroll>
<Text colSpan={columns=>columns / 3}
rowSpan={4}
text={'Release'}
@@ -119,7 +120,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
items={Constants.RELEASE_TYPES}
row={5}
rowSpan={7}
selected={props.Release}/>
selected={Constants.RELEASE_TYPES[props.Release]}/>
<Text col={dimensions => dimensions.columns / 3}
colSpan={remain=>remain / 2}
rowSpan={4}
@@ -138,7 +139,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
items={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]]}
row={5}
rowSpan={7}
selected={props.ReleaseVersion}/>
selected={props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion]}/>
{optionsDisplay}
</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;
}
.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 {
background: rgba(10, 10, 15, 0.9);
border-color: rgba(10, 10, 20, 0.9);

View File

@@ -4,13 +4,16 @@ import './DropDown.css';
export default props => {
const options = props.items.map((s, i) => {
return (
<option className={'DropDownOption'} key={i} value={i}>{s}</option>
<option className={'DropDownOption'} key={i} value={s}>{s}</option>
);
});
return (
<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}
</select>
</div>

View File

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

View File

@@ -8,8 +8,8 @@ export default props => {
className={'Loading'}>
<div className={'LoadingContent'}>
<Loader color={'var(--heading_text_color)'}
height='28px'
width='28px'
height={28}
width={28}
type='ThreeDots'/>
</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' +
'-----END PUBLIC KEY-----';
const REPERTORY_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.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 = [
'ubuntu18.04',
'ubuntu18.10',
'ubuntu19.04'
'ubuntu19.04',
'ubuntu19.10'
];
exports.DATA_LOCATIONS = {
@@ -50,12 +50,12 @@ exports.DATA_LOCATIONS = {
exports.PROVIDER_LIST = [
'Sia',
'SiaPrime'
'ScPrime'
];
exports.PROVIDER_ARG = {
sia: '',
siaprime: '-sp'
scprime: '-sp'
};
exports.RELEASE_TYPES = [
@@ -87,8 +87,8 @@ exports.IPC_Check_Mount_Location = 'check_mount_location';
exports.IPC_Delete_File = 'delete_file';
exports.IPC_Detect_Mounts = 'detect_mounts';
exports.IPC_Detect_Mounts_Reply = 'detect_mounts_reply';
exports.IPC_Detect_Mount = 'detect_mount';
exports.IPC_Detect_Mount_Reply = 'detect_mount_reply';
exports.IPC_Download_File = 'download_file';
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_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_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_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 {
width: 90vw;
height: 90vh;
padding: 4px;
width: calc(100vw - (var(--default_spacing) * 4));
height: calc(100vh - (var(--default_spacing) * 4));
padding: var(--default_spacing);
margin: 0;
}

View File

@@ -3,7 +3,7 @@ import './Configuration.css';
import {connect} from 'react-redux';
import Box from '../../components/UI/Box/Box';
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 IPCContainer from '../IPCContainer/IPCContainer';
import {displayConfiguration} from '../../redux/actions/mount_actions';
@@ -20,6 +20,7 @@ class Configuration extends IPCContainer {
ObjectLookup: {},
OriginalItemList: [],
OriginalObjectLookup: {},
IsRemoteMount: false,
ItemList: [],
Saving: false,
ShowAdvanced: false,
@@ -70,6 +71,7 @@ class Configuration extends IPCContainer {
this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply);
this.sendRequest(Constants.IPC_Get_Config_Template, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
Version: this.props.version,
});
}
@@ -86,8 +88,12 @@ class Configuration extends IPCContainer {
.map(key => {
return {
advanced: template[key] ? template[key].advanced : false,
hide_remote: template[key] ? template[key].hide_remote : false,
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=> {
@@ -98,7 +104,6 @@ class Configuration extends IPCContainer {
}
return ret;
});
return {
ObjectList: objectList,
ItemList: itemList,
@@ -140,9 +145,20 @@ class Configuration extends IPCContainer {
const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template);
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({
IsRemoteMount: isRemoteMount,
ItemList: list.ItemList,
ObjectLookup: objectLookup,
OriginalItemList: itemListCopy,
@@ -160,6 +176,7 @@ class Configuration extends IPCContainer {
}, ()=> {
this.sendRequest(Constants.IPC_Get_Config, {
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
Version: this.props.version,
});
});
@@ -202,17 +219,37 @@ class Configuration extends IPCContainer {
this.sendRequest(Constants.IPC_Set_Config_Values, {
Items: changedItems,
Provider: this.props.DisplayConfiguration,
Remote: this.props.DisplayRemoteConfiguration,
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() {
let confirmSave = null;
if ((this.state.ChangedItems.length > 0) || this.state.ChangedObjectLookup) {
confirmSave = (
<Modal>
<Box dxStyle={{width: '40vw', padding: '4px'}}>
<Box dxStyle={{width: '40vw', padding: 'var(--default_spacing)'}}>
<h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1>
<table width='100%'><tbody>
<tr>
@@ -228,7 +265,7 @@ class Configuration extends IPCContainer {
const configurationItems = this.state.ItemList
.map((k, i) => {
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}
changed={e=>this.handleItemChanged(e, i)}
grouping={'Settings'}
@@ -249,13 +286,14 @@ class Configuration extends IPCContainer {
{
this.state.ObjectLookup[key].map((k, i) => {
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}
changed={e=>this.handleObjectItemChanged(e, key, i)}
grouping={key}
items={this.state.Template[key].template[k.label].items}
key={i}
label={k.label}
readOnly={this.state.IsRemoteMount && ((k.label === 'RemoteHostNameOrIp') || (k.label === 'RemotePort'))}
template={this.state.Template[key].template[k.label]}
value={k.value}/> :
null)
@@ -269,12 +307,15 @@ class Configuration extends IPCContainer {
return (
<div className={'Configuration'}>
{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'}}>
<b style={{cursor: 'pointer'}}
onClick={this.checkSaveRequired}>X</b>
</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%'}}>
{objectItems}
{(configurationItems.length > 0) ? <h1>Settings</h1> : null}
@@ -289,13 +330,15 @@ class Configuration extends IPCContainer {
const mapStateToProps = state => {
return {
DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
Platform: state.common.Platform,
}
};
const mapDispatchToProps = dispatch => {
return {
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 './ConfigurationItem.css';
import settings from '../../assets/settings';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
import CheckBox from '../../../components/UI/CheckBox/CheckBox';
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 => {
return {
@@ -36,13 +38,14 @@ export default connect(null, mapDispatchToProps)(props => {
let data;
switch (props.template.type) {
case "bool":
data = <input checked={JSON.parse(props.value)}
onChange={e=>handleChanged(e)}
type={'checkbox'}/>;
data = <CheckBox changed={handleChanged}
checked={props.value}
disabled={props.readOnly}/>;
break;
case "double":
data = <input min={0.0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
step={"0.01"}
className={'ConfigurationItemInput'}
@@ -51,24 +54,18 @@ export default connect(null, mapDispatchToProps)(props => {
break;
case "list":
const options = props.items.map((s, i) => {
return (
<option className={'ConfigurationItemOption'} key={i} value={s}>{s}</option>
);
});
data = (
<select onChange={e=>handleChanged(e)}
className={'ConfigurationItemSelect'}
value={props.value}>
{options}
</select>
);
data = <DropDown alt
auto
changed={handleChanged}
disabled={props.readOnly}
items={props.items}
selected={props.value} />;
break;
case "string":
data = <input onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'}
disabled={props.readOnly}
type={'text'}
value={props.value}/>;
break;
@@ -76,6 +73,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint8":
data = <input max={255}
min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
@@ -85,6 +83,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint16":
data = <input max={65535}
min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
@@ -94,6 +93,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint32":
data = <input max={4294967295}
min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}
@@ -103,6 +103,7 @@ export default connect(null, mapDispatchToProps)(props => {
case "uint64":
data = <input max={18446744073709551615}
min={0}
disabled={props.readOnly}
onChange={e=>handleChanged(e)}
className={'ConfigurationItemInput'}
type={'number'}

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,21 +48,25 @@ class SelectAppPlatform extends IPCContainer {
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() {
return (
<Box dxDark dxStyle={{padding: '8px'}}>
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
<h1 className={'SAPHeading'}>Select Linux Platform</h1>
<div className={'SAPContent'}>
<p>Repertory was unable to detect your Linux distribution. Please select one of the following and click <b>Test</b> to continue:</p>
</div>
<div className={'SAPActions'}>
<DropDown changed={e => this.setState({
...this.state,
Selected: e.target.value
})}
<DropDown changed={this.handleChanged}
disabled={this.props.InstallTestActive}
items={Constants.LINUX_SELECTABLE_PLATFORMS}
selected={this.state.Selected}/>
selected={Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]}/>
<Button clicked={this.handleTestClicked}
disabled={this.props.InstallTestActive}>Test</Button>
</div>

View File

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

View File

@@ -1,21 +1,22 @@
:root {
--border_radius: 4px;
--control_background: rgba(125, 145, 200, .15);
--control_background_hover: rgba(125, 145, 200, .30);
--control_border: 1px solid rgba(70, 70, 70, 0.9);
--control_box_shadow: 1px 1px 1px black;
--control_transparent_background: rgba(30, 30, 50, 0.3);
--control_dark_transparent_background: rgba(10, 10, 20, 0.8);
--control_background: rgba(105, 105, 150, 0.2);
--control_background_hover: rgba(105, 105, 150, 0.4);
--control_border: 1px solid rgba(80, 80, 90, 0.9);
--control_box_shadow: 2px 2px 2px black;
--control_transparent_background: rgba(10, 10, 16, 0.5);
--control_dark_transparent_background: rgba(10, 10, 16, 0.7);
--text_color: rgba(200, 205, 225, 0.8);
--text_color_hover: rgba(200, 205, 225, 0.9);
--text_color_error: rgba(203, 120, 120, 0.8);
--heading_text_color: rgba(146, 175, 220, 0.7);
--text_color: rgba(200, 200, 240, 0.7);
--text_color_hover: rgba(200, 200, 225, 0.7);
--text_color_error: rgba(203, 120, 120, 0.7);
--heading_text_color: rgba(132, 160, 230, 0.7);
--heading_other_text_color: var(--heading_text_color);
--text_color_transition: color 0.3s;
--default_font_size: 14px
--default_font_size: 14px;
--default_spacing: 8px;
}
* {
@@ -43,8 +44,8 @@ p {
padding: 0;
margin: 0;
color: var(--text_color);
font-size: medium;
font-weight: bold;
font-size: var(--default_font_size);
font-weight: normal;
text-align: center;
}
@@ -78,7 +79,7 @@ p {
}
.scrollable-content, ::-webkit-scrollbar {
width: 10px;
width: 8px;
}
.scrollable-content, ::-webkit-scrollbar * {
@@ -86,5 +87,5 @@ p {
}
.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;
if (ipcRenderer) {
ipcRenderer.on(Constants.IPC_Get_Platform_Reply, (event, arg) => {
if (arg.Platform === 'linux') {
ipcRenderer.once(Constants.IPC_Get_Platform_Reply, (event, platformInfo) => {
if (platformInfo.Platform === 'linux') {
const root = document.documentElement;
root.style.setProperty('--default_font_size', '15.3px');
}
store = createAppStore(arg.Platform, arg.AppPlatform, packageJson.version);
ipcRenderer.on(Constants.IPC_Get_State_Reply, (event, arg) => {
if (arg.data) {
store.dispatch(setActiveRelease(arg.data.Release, arg.data.Version));
for (const provider of Constants.PROVIDER_LIST) {
let state = arg.data[provider] || this.props.ProviderState[provider];
ipcRenderer.once(Constants.IPC_Get_State_Reply, (event, result) => {
if (result.data) {
store = createAppStore(platformInfo, packageJson.version, result.data);
store.dispatch(setActiveRelease(result.data.Release, result.data.Version));
const providerList = [
...Constants.PROVIDER_LIST,
...store.getState().mounts.RemoteMounts,
];
for (const provider of providerList) {
let state = result.data[provider] || store.getState().mounts.ProviderState[provider];
if (state.AutoMount === undefined) {
state['AutoMount'] = false;
}
@@ -37,7 +40,10 @@ if (ipcRenderer) {
}
store.dispatch(setProviderState(provider, state));
}
} else {
store = createAppStore(platformInfo, packageJson.version, {});
}
ReactDOM.render((
<Provider store={store}>
<App/>

View File

@@ -3,6 +3,26 @@ import {createAction} from 'redux-starter-kit';
import {getIPCRenderer} from '../../utils';
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 => {
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 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 setApplicationReady = createAction('common/setApplicationReady');
@@ -60,26 +126,4 @@ export const shutdownApplication = () => {
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 {createAction} from 'redux-starter-kit';
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 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 SET_MOUNT_STATE = 'mounts/setMountState';

View File

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

View File

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

View File

@@ -1,124 +1,194 @@
import * as Constants from '../../constants';
import {createReducer} from 'redux-starter-kit';
import {
displayConfiguration,
addRemoteMount2,
DISPLAY_CONFIGURATION,
removeRemoteMount3,
RESET_MOUNTS_STATE,
SET_ALLOW_MOUNT,
setAutoMountProcessed,
SET_AUTO_MOUNT_PROCESSED,
setBusy,
SET_MOUNT_STATE,
SET_MOUNTED,
SET_PROVIDER_STATE
} from '../actions/mount_actions';
const providerState = Constants.PROVIDER_LIST.map(provider=> {
return {
[provider]: {
AutoMount: false,
AutoRestart: false,
MountLocation: '',
export const createMountReducer = state => {
let providerList = [
...Constants.PROVIDER_LIST,
...(state.RemoteMounts||[]),
];
const providerState = providerList.map(provider=> {
return {
[provider]: {
AutoMount: false,
AutoRestart: false,
MountLocation: '',
}
}
}
}).reduce((map, obj) => {
return {
...map,
...obj
}
});
}).reduce((map, obj) => {
return {
...map,
...obj
}
});
const mountState = Constants.PROVIDER_LIST.map(provider => {
return {
[provider]: {
AllowMount: false,
DriveLetters: [],
Mounted: false,
const mountState = providerList.map(provider => {
return {
[provider]: {
AllowMount: false,
DriveLetters: [],
Mounted: false,
}
}
}
}).reduce((map, obj) => {
return {
...map,
...obj
}
});
}).reduce((map, obj) => {
return {
...map,
...obj
}
});
export const mountReducer = createReducer({
AutoMountProcessed: false,
DisplayConfiguration: null,
MountsBusy: false,
MountState: mountState,
ProviderState: providerState,
}, {
[displayConfiguration]: (state, action) => {
const autoMountProcessed = providerList.map(provider => {
return {
...state,
DisplayConfiguration: action.payload
};
},
[RESET_MOUNTS_STATE]: (state, action) => {
return {
...state,
MountsBusy: false,
MountState: mountState,
[provider]: false,
}
},
[setAutoMountProcessed]: (state, action) => {
}).reduce((map, obj) => {
return {
...state,
AutoMountProcessed: action.payload
};
},
[SET_ALLOW_MOUNT]: (state, action) => {
return {
...state,
MountState: {
...state.MountState,
[action.payload.provider]: {
...state.MountState[action.payload.provider],
AllowMount: action.payload.allow,
...map,
...obj
}
});
return createReducer({
AutoMountProcessed: autoMountProcessed,
DisplayConfiguration: null,
DisplayRemoteConfiguration: false,
MountsBusy: false,
MountState: mountState,
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,
}
}
};
},
[setBusy]: (state, action) => {
return {
...state,
MountsBusy: action.payload
};
},
[SET_MOUNT_STATE]: (state, action) => {
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,
};
},
[SET_ALLOW_MOUNT]: (state, action) => {
return {
...state,
MountState: {
...state.MountState,
[action.payload.provider]: {
...state.MountState[action.payload.provider],
AllowMount: action.payload.allow,
}
}
}
};
},
[SET_PROVIDER_STATE]: (state, action) => {
return {
...state,
ProviderState: {
...state.ProviderState,
[action.payload.provider]: {
...state.ProviderState[action.payload.provider],
...action.payload.state
},
}
};
}
});
};
},
[setBusy]: (state, action) => {
return {
...state,
MountsBusy: action.payload
};
},
[SET_MOUNT_STATE]: (state, action) => {
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,
}
}
};
},
[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 {errorReducer} from '../reducers/error_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';
export default function createAppStore(platform, appPlatform, version) {
export default function createAppStore(platformInfo, version, state) {
const reducer = {
common: createCommonReducer(platform, appPlatform, version),
common: createCommonReducer(platformInfo, version),
download: downloadReducer,
error: errorReducer,
install: installReducer,
mounts: mountReducer,
mounts: createMountReducer(state),
relver: releaseVersionReducer,
};

View File

@@ -4,7 +4,7 @@ const helpers = require('../../helpers');
const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
helpers
.getConfig(data.Version, data.Provider)
.getConfig(data.Version, data.Provider, data.Remote)
.then((data) => {
if (data.Code === 0) {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {
@@ -21,7 +21,7 @@ const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => {
helpers
.getConfigTemplate(data.Version, data.Provider)
.getConfigTemplate(data.Version, data.Provider, data.Remote)
.then((data) => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {
Template: data,
@@ -36,7 +36,7 @@ const addListeners = (ipcMain, standardIPCReply) => {
const setConfigValue = (i) => {
if (i < data.Items.length) {
helpers
.setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Version)
.setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Remote, data.Version)
.then(() => {
setConfigValue(++i);
})

View File

@@ -72,7 +72,15 @@ const addListeners = (ipcMain, standardIPCReply) => {
};
if (data.IsWinFSP) {
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 => {
if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
@@ -99,4 +107,4 @@ const addListeners = (ipcMain, standardIPCReply) => {
module.exports = {
addListeners
};
};

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