Merged 1.3.x_branch into master
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ build/
|
||||
chrome_data/
|
||||
dist/
|
||||
/.cache
|
||||
/temp.json
|
||||
/.eslintcache
|
||||
|
||||
14
.vim/coc-settings.json
Normal file
14
.vim/coc-settings.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"HKEY",
|
||||
"HKLM",
|
||||
"Redistributable",
|
||||
"Skylinks",
|
||||
"Skynet",
|
||||
"Unmount",
|
||||
"msiexec",
|
||||
"relver",
|
||||
"siaprime",
|
||||
"skylink"
|
||||
]
|
||||
}
|
||||
7
.vimrc
Normal file
7
.vimrc
Normal file
@@ -0,0 +1,7 @@
|
||||
set autoread
|
||||
set path+=.,public/**,src/**,test/**
|
||||
if has('win32')
|
||||
let &makeprg="create_dist.cmd"
|
||||
else
|
||||
let &makeprg="./create_dist.sh"
|
||||
endif
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,4 +1,29 @@
|
||||
# Changelog
|
||||
## 1.3.2
|
||||
* \#48: Support pinning files to cache
|
||||
* Fixed Skynet export display
|
||||
* Properly detect existing remote
|
||||
* Reduced number of Linux binaries to:
|
||||
* CentOS 7
|
||||
* Solus
|
||||
* S3 mount support [disabled]
|
||||
|
||||
## 1.3.1
|
||||
* \#45: Skynet mount support
|
||||
|
||||
## 1.3.0
|
||||
* \#38: Enhance new repertory release available notification
|
||||
* \#46: Fix Mount Manager unmount and mount detection
|
||||
* Skynet support
|
||||
* Synchronize UI major/minor version with `repertory` major/minor version
|
||||
* Added `Password` component
|
||||
* Reduced number of Linux binaries to:
|
||||
* CentOS 7
|
||||
* Debian 9
|
||||
* Debian 10
|
||||
* Solus
|
||||
* Added `FocusTrap` to modals
|
||||
|
||||
## 1.1.4
|
||||
* \#39: Cleanup old releases and UI upgrades
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Repertory UI MIT License
|
||||
### Copyright <2018-2019> <scott.e.graves@protonmail.com>
|
||||
### Copyright <2018-2020> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
54
README.md
54
README.md
@@ -1,57 +1,39 @@
|
||||
# Repertory UI
|
||||

|
||||

|
||||
|
||||
## GUI for [Repertory](https://bitbucket.org/blockstorage/repertory)
|
||||
Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.
|
||||
|
||||
# Windows Issue
|
||||
If you're experiencing slow copy, read, and/or write, please install the following UI version. This is a special build that includes WinFSP 2020.2:
|
||||
|
||||
* [Repertory UI v1.1.5 Windows 64-bit](https://bitbucket.org/blockstorage/repertory-testing/downloads/repertory-ui_1.1.5_win.exe)
|
||||
|
||||
# Discord Invite
|
||||
* [Repertory Discord](https://discord.gg/f2rCfqRgrJ)
|
||||
Repertory allows you to mount Sia, Skynet and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.
|
||||
|
||||
## Requirements
|
||||
* Sia >=1.4.1
|
||||
* ScPrime >=1.4.1.2
|
||||
|
||||
## Downloads
|
||||
* **Repertory UI v1.1.4 Linux 64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage)
|
||||
* NOTE: Linux distributions require `fuse` and `libfuse` to be installed.
|
||||
* **Repertory UI v1.1.4 OS X 64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_mac.dmg)
|
||||
* **Repertory UI v1.1.4 Windows 64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_win.exe)
|
||||
* **Repertory UI v1.3.2 Linux
|
||||
64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_linux_x86_64.AppImage)
|
||||
* NOTE: Linux distributions require `fuse` and `libfuse` to be installed.
|
||||
* **Repertory UI v1.3.2 OS X
|
||||
64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_mac.dmg)
|
||||
* **Repertory UI v1.3.2 Windows
|
||||
64-bit** [<Download\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_win.exe)
|
||||
|
||||
## Supported Platforms
|
||||
* OS X 64-bit
|
||||
* Windows 64-bit
|
||||
* Linux 64-bit Distributions:
|
||||
* Antergos
|
||||
* Uses `Ubuntu 18.10` binaries for compatibility
|
||||
* Arch Linux
|
||||
* Bodhi 5.0.0
|
||||
* CentOS 7
|
||||
* CentOS 8
|
||||
* Debian 9
|
||||
* Debian 10
|
||||
* Elementary OS 5.0
|
||||
* Fedora 28
|
||||
* Fedora 29
|
||||
* Fedora 30
|
||||
* Fedora 31
|
||||
* Linux Mint 19
|
||||
* Linux Mint 19.1
|
||||
* Linux Mint 19.2
|
||||
* CentOS 7, 8
|
||||
* Debian 9, 10
|
||||
* Elementary OS 5.0, 5.1
|
||||
* Fedora 28, 29, 30, 31, 32, 33
|
||||
* Linux Mint 19, 19.1, 19.2, 19.3, 20, 20.1
|
||||
* Manjaro
|
||||
* Uses `Ubuntu 18.10` binaries for compatibility
|
||||
* OpenSUSE Leap 15.0
|
||||
* OpenSUSE Leap 15.1
|
||||
* OpenSUSE Leap 15.0, 15.1
|
||||
* OpenSUSE Tumbleweed
|
||||
* Solus
|
||||
* Ubuntu 18.04
|
||||
* Ubuntu 18.10
|
||||
* Ubuntu 19.04
|
||||
* Ubuntu 19.10
|
||||
* Ubuntu 18.04, 18.10, 19.04, 19.10, 20.04
|
||||
|
||||
## Issues/Suggestions
|
||||
Please submit [here](https://bitbucket.org/blockstorage/repertory-ui/issues?status=new&status=open)
|
||||
@@ -63,10 +45,6 @@ This feature allows you to share your mounts with other PC's and mount them remo
|
||||
### 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.
|
||||
|
||||
## Tips
|
||||
* BTC: 1CMvhGaJfH95VS4CTS6dJYDs8kwYXTUCEA
|
||||
* SC: c5510db5c2e85794b9cb81851daa57d6dfd5b4e42cd02789442e8789cd6492b07c754b2056ed
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
@echo off
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
set ROOT=%~dp0%
|
||||
set /a ENABLE_UPLOAD=%1
|
||||
set BITBUCKET_AUTH=%2
|
||||
|
||||
set BITBUCKET_TESTING=%3
|
||||
if "%BITBUCKET_TESTING%" == "1" (
|
||||
set REPOSITORY=repertory-testing
|
||||
) else (
|
||||
set REPOSITORY=repertory-ui
|
||||
)
|
||||
set OPENSSL_BIN="c:\OpenSSL-Win64\bin\openssl.exe"
|
||||
if NOT EXIST %OPENSSL_BIN% (
|
||||
set OPENSSL_BIN="c:\Program Files\OpenSSL-Win64\bin\openssl.exe"
|
||||
@@ -41,23 +46,17 @@ pushd "%ROOT%"
|
||||
|
||||
del /q upload_response.json 1>NUL 2>&1
|
||||
|
||||
("%CURL_BIN%" -F name="%OUT_FILE%" -F anonymous=true -F file="@%OUT_FILE%" https://pixeldrain.com/api/file > upload_response.json) || (
|
||||
call :PIXEL_RESPONSE 0
|
||||
)
|
||||
call :PIXEL_RESPONSE 1
|
||||
set PIXEL_LOCATION=https://pixeldrain.com/api/file/!PIXEL_ID!
|
||||
|
||||
call :UPLOAD_TO_BITBUCKET "%OUT_FILE%"
|
||||
call :UPLOAD_TO_BITBUCKET "%OUT_FILE%.sha256"
|
||||
call :UPLOAD_TO_BITBUCKET "%OUT_FILE%.sig"
|
||||
set BITBUCKET_LOCATION=https://bitbucket.org/blockstorage/repertory-ui/downloads/%OUT_FILE%
|
||||
set BITBUCKET_LOCATION=https://bitbucket.org/blockstorage/%REPOSITORY%/downloads/%OUT_FILE%
|
||||
|
||||
del /q releases.json 1>NUL 2>&1
|
||||
|
||||
("%JQ_BIN%" ".Versions.win32|=(.+ ["""%APP_VER%"""]|unique)" ..\releases.json>releases_temp.json && move /Y releases_temp.json releases.json 1>NUL 2>&1) || (call :ERROR "Update releases.json Versions failed")
|
||||
("%JQ_BIN%" ".Locations.win32."""%APP_VER%""".sig="""!APP_SIG!"""" releases.json>releases_temp.json && move /Y releases_temp.json releases.json 1>NUL 2>&1) || (call :ERROR "Update releases.json sig failed")
|
||||
("%JQ_BIN%" ".Locations.win32."""%APP_VER%""".sha256="""!APP_SHA256!"""" releases.json>releases_temp.json && move /Y releases_temp.json releases.json 1>NUL 2>&1) || (call :ERROR "Update releases.json sha256 failed")
|
||||
("%JQ_BIN%" ".Locations.win32."""%APP_VER%""".urls=["""!PIXEL_LOCATION!""","""!BITBUCKET_LOCATION!"""]" releases.json>releases_temp.json && move /Y releases_temp.json releases.json 1>NUL 2>&1) || (call :ERROR "Update releases.json URL failed")
|
||||
("%JQ_BIN%" ".Locations.win32."""%APP_VER%""".urls=["""!BITBUCKET_LOCATION!"""]" releases.json>releases_temp.json && move /Y releases_temp.json releases.json 1>NUL 2>&1) || (call :ERROR "Update releases.json URL failed")
|
||||
)
|
||||
popd
|
||||
) || (
|
||||
@@ -65,41 +64,17 @@ pushd "%ROOT%"
|
||||
)
|
||||
popd
|
||||
goto :END
|
||||
|
||||
|
||||
:NO_QUOTES
|
||||
set %~1=!%~1:"=!
|
||||
goto :EOF
|
||||
|
||||
:PIXEL_RESPONSE
|
||||
set PIXEL_RESPONSE=
|
||||
if %1==1 (
|
||||
for /f "delims=" %%i in ('%JQ_BIN% .success upload_response.json') do (
|
||||
if "%%i" == "false" (
|
||||
for /f "delims=" %%i in ('%JQ_BIN% .message upload_response.json') do (
|
||||
set PIXEL_RESPONSE=!PIXEL_RESPONSE!%%i
|
||||
)
|
||||
call :ERROR "Upload to pixeldrain failed: !PIXEL_RESPONSE!"
|
||||
) else (
|
||||
for /f "delims=" %%i in ('%JQ_BIN% .id upload_response.json') do (
|
||||
set PIXEL_ID=%%i
|
||||
call :NO_QUOTES PIXEL_ID
|
||||
)
|
||||
)
|
||||
)
|
||||
) else (
|
||||
for /f "delims=" %%i in ('type upload_response.json') do (
|
||||
set PIXEL_RESPONSE=!PIXEL_RESPONSE!%%i
|
||||
)
|
||||
call :ERROR "Upload to pixeldrain failed: !PIXEL_RESPONSE!"
|
||||
)
|
||||
goto :EOF
|
||||
|
||||
:UPLOAD_TO_BITBUCKET
|
||||
set SOURCE_FILE=%1
|
||||
call :NO_QUOTES SOURCE_FILE
|
||||
call :NO_QUOTES BITBUCKET_AUTH
|
||||
echo "Uploading !SOURCE_FILE! to Bitbucket"
|
||||
(curl --fail -u "!BITBUCKET_AUTH!" -X POST https://api.bitbucket.org/2.0/repositories/blockstorage/repertory-ui/downloads -F files="@!SOURCE_FILE!" > upload_response.json) || (call :ERROR "Upload to Bitbucket failed: %SOURCE_FILE%")
|
||||
echo "Uploading !SOURCE_FILE! to Bitbucket %REPOSITORY%"
|
||||
(curl --fail -u "!BITBUCKET_AUTH!" -X POST https://api.bitbucket.org/2.0/repositories/blockstorage/%REPOSITORY%/downloads -F files="@!SOURCE_FILE!" > upload_response.json) || (call :ERROR "Upload to Bitbucket %REPOSITORY% failed: %SOURCE_FILE%")
|
||||
goto :EOF
|
||||
|
||||
:ERROR
|
||||
@@ -108,9 +83,9 @@ goto :EOF
|
||||
if "%DISABLE_PAUSE%" NEQ "1" (
|
||||
pause
|
||||
)
|
||||
goto :END
|
||||
goto :END
|
||||
|
||||
:END
|
||||
if "!ERROR_EXIT!" NEQ "0" (
|
||||
exit !ERROR_EXIT!
|
||||
)
|
||||
)
|
||||
|
||||
@@ -7,6 +7,13 @@ export PATH
|
||||
|
||||
ENABLE_UPLOAD=$1
|
||||
BITBUCKET_AUTH=$2
|
||||
BITBUCKET_TESTING=$3
|
||||
|
||||
if [ "$BITBUCKET_TESTING" = "1" ]; then
|
||||
REPOSITORY=repertory-testing
|
||||
else
|
||||
REPOSITORY=repertory-ui
|
||||
fi
|
||||
|
||||
PRIVATE_KEY=../../blockstorage_dev_private.pem
|
||||
PUBLIC_KEY=../blockstorage_dev_public.pem
|
||||
@@ -19,7 +26,7 @@ if beginsWith darwin "$OSTYPE"; then
|
||||
JQ_EXEC=jq-osx-amd64
|
||||
SHA256_EXEC="shasum -a 256 -b"
|
||||
else
|
||||
DISTRO_LIST="arch centos7 centos8 debian9 debian10 fedora28 fedora29 fedora30 fedora31 opensuse15 opensuse15.1 solus tumbleweed ubuntu18.04 ubuntu18.10 ubuntu19.04 ubuntu19.10"
|
||||
DISTRO_LIST="centos7 debian9 debian10 solus"
|
||||
OUT_FILE=repertory-ui_${APP_VER}_linux_x86_64.AppImage
|
||||
BASE64_EXEC="base64 -w0"
|
||||
JQ_EXEC=jq-linux64
|
||||
@@ -33,7 +40,8 @@ exit_script() {
|
||||
|
||||
upload_to_bitbucket() {
|
||||
SOURCE_FILE=$1
|
||||
curl --fail -u "${BITBUCKET_AUTH}" -X POST https://api.bitbucket.org/2.0/repositories/blockstorage/repertory-ui/downloads -F files=@${SOURCE_FILE} > upload_response.json || exit_script "Upload to Bitbucket failed: ${SOURCE_FILE}"
|
||||
echo "Uploading ${SOURCE_FILE} to Bitbucket ${REPOSITORY}"
|
||||
curl --fail -u "${BITBUCKET_AUTH}" -X POST https://api.bitbucket.org/2.0/repositories/blockstorage/${REPOSITORY}/downloads -F files=@${SOURCE_FILE} > upload_response.json || exit_script "Upload to Bitbucket failed: ${SOURCE_FILE}"
|
||||
}
|
||||
|
||||
chmod +x "bin/${JQ_EXEC}" || exit_script "chmod +x ${JQ_EXEC} failed"
|
||||
@@ -52,30 +60,20 @@ if npm run dist; then
|
||||
APP_SHA256=$(cat ${OUT_FILE}.sha256 | awk '{print $1;}')
|
||||
|
||||
rm -f upload_response.json 1>/dev/null 2>&1
|
||||
curl --fail -F name="${OUT_FILE}" -F anonymous=true -F file="@${OUT_FILE}" https://pixeldrain.com/api/file > upload_response.json || exit_script "Upload to Pixeldrain failed"
|
||||
|
||||
PIXEL_SUCCESS=$(${JQ_EXEC} .success upload_response.json)
|
||||
if [ "${PIXEL_SUCCESS}" = "false" ]; then
|
||||
PIXEL_MESSAGE=$(${JQ_EXEC} .message upload_response.json)
|
||||
exit_script "${PIXEL_MESSAGE}"
|
||||
else
|
||||
PIXEL_ID=$(${JQ_EXEC} .id upload_response.json|sed s/\"//g)
|
||||
PIXEL_LOCATION=https://pixeldrain.com/api/file/${PIXEL_ID}
|
||||
upload_to_bitbucket "${OUT_FILE}"
|
||||
upload_to_bitbucket "${OUT_FILE}.sha256"
|
||||
upload_to_bitbucket "${OUT_FILE}.sig"
|
||||
BITBUCKET_LOCATION=https://bitbucket.org/blockstorage/${REPOSITORY}/downloads/${OUT_FILE}
|
||||
|
||||
upload_to_bitbucket "${OUT_FILE}"
|
||||
upload_to_bitbucket "${OUT_FILE}.sha256"
|
||||
upload_to_bitbucket "${OUT_FILE}.sig"
|
||||
BITBUCKET_LOCATION=https://bitbucket.org/blockstorage/repertory-ui/downloads/${OUT_FILE}
|
||||
|
||||
cp -f ../releases.json ./releases.json
|
||||
for DISTRONAME in ${DISTRO_LIST}; do
|
||||
${JQ_EXEC} ".Versions[\"${DISTRONAME}\"]|=(.+ [\"${APP_VER}\"]|unique)" releases.json > releases_temp.json || exit_script "Update releases.json Versions failed"
|
||||
${JQ_EXEC} ".Locations[\"${DISTRONAME}\"].\"${APP_VER}\".sig=\"${APP_SIG}\"" releases_temp.json > releases.json || exit_script "Update releases.json sig failed"
|
||||
${JQ_EXEC} ".Locations[\"${DISTRONAME}\"].\"${APP_VER}\".sha256=\"${APP_SHA256}\"" releases.json > releases_temp.json || exit_script "Update releases.json sha256 failed"
|
||||
${JQ_EXEC} ".Locations[\"${DISTRONAME}\"].\"${APP_VER}\".urls=[\"${PIXEL_LOCATION}\",\"${BITBUCKET_LOCATION}\"]" releases_temp.json > releases.json || exit_script "Update releases.json URL failed"
|
||||
done
|
||||
rm -f releases_temp.json
|
||||
fi
|
||||
cp -f ../releases.json ./releases.json
|
||||
for DISTRONAME in ${DISTRO_LIST}; do
|
||||
${JQ_EXEC} ".Versions[\"${DISTRONAME}\"]|=(.+ [\"${APP_VER}\"]|unique)" releases.json > releases_temp.json || exit_script "Update releases.json Versions failed"
|
||||
${JQ_EXEC} ".Locations[\"${DISTRONAME}\"].\"${APP_VER}\".sig=\"${APP_SIG}\"" releases_temp.json > releases.json || exit_script "Update releases.json sig failed"
|
||||
${JQ_EXEC} ".Locations[\"${DISTRONAME}\"].\"${APP_VER}\".sha256=\"${APP_SHA256}\"" releases.json > releases_temp.json || exit_script "Update releases.json sha256 failed"
|
||||
${JQ_EXEC} ".Locations[\"${DISTRONAME}\"].\"${APP_VER}\".urls=[\"${BITBUCKET_LOCATION}\"]" releases_temp.json > releases.json || exit_script "Update releases.json URL failed"
|
||||
done
|
||||
rm -f releases_temp.json
|
||||
fi
|
||||
cd -
|
||||
else
|
||||
|
||||
52
package.json
52
package.json
@@ -1,43 +1,43 @@
|
||||
{
|
||||
"name": "repertory-ui",
|
||||
"version": "1.1.4",
|
||||
"version": "1.3.2",
|
||||
"private": true,
|
||||
"author": "scott.e.graves@protonmail.com",
|
||||
"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.",
|
||||
"description": "GUI for Repertory - Repertory allows you to mount Sia, Skynet, and/or ScPrime storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.8",
|
||||
"@reduxjs/toolkit": "^1.2.4",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.13",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"auto-launch": "^5.0.5",
|
||||
"axios": "^0.19.2",
|
||||
"axios": "^0.21.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-log": "^4.0.6",
|
||||
"electron-debug": "^3.1.0",
|
||||
"electron-log": "^4.3.0",
|
||||
"focus-trap-react": "^8.3.2",
|
||||
"font-awesome": "^4.7.0",
|
||||
"node-schedule": "^1.3.2",
|
||||
"node-cron": "^1.2.1",
|
||||
"randomstring": "^1.1.5",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-loader-spinner": "^3.1.5",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-scripts": "3.3.1",
|
||||
"react-tooltip": "^4.0.3",
|
||||
"react": "^16.14.0",
|
||||
"react-checkbox-tree": "^1.6.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-loader-spinner": "^3.1.14",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-tooltip": "^4.2.11",
|
||||
"redux": "^4.0.5",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"unzipper": "^0.10.8",
|
||||
"unzipper": "^0.10.11",
|
||||
"winreg": "^1.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.0",
|
||||
"electron": "^5.0.13",
|
||||
"electron-builder": "^20.44.4",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"fibers": "^4.0.2",
|
||||
"node-sass": "^4.13.1",
|
||||
"sass": "^1.25.0",
|
||||
"typescript": "^3.7.5",
|
||||
"webpack-browser-plugin": "^1.0.20"
|
||||
"babel-loader": "8.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "5.0.13",
|
||||
"electron-builder": "22.9.1",
|
||||
"electron-webpack": "^2.8.2",
|
||||
"webpack": "4.44.2",
|
||||
"webpack-dev-server": "3.11.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
84
public/detect_linux.sh
Normal file → Executable file
84
public/detect_linux.sh
Normal file → Executable file
@@ -8,89 +8,23 @@ resetDistVer() {
|
||||
DISTVER=
|
||||
}
|
||||
|
||||
if [ -f /etc/centos-release ]; then
|
||||
. /etc/os-release
|
||||
if [ "$VERSION_ID" = "7" ]; then
|
||||
DISTNAME=centos
|
||||
DISTVER=7
|
||||
elif [ "$VERSION_ID" = "8" ]; then
|
||||
DISTNAME=centos
|
||||
DISTVER=8
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
elif [ -f /etc/fedora-release ]; then
|
||||
. /etc/os-release
|
||||
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
|
||||
elif [ -f /etc/lsb-release ]; then
|
||||
. /etc/lsb-release
|
||||
DISTNAME=$(echo ${DISTRIB_ID} | awk '{print tolower($0)}')
|
||||
DISTVER=${DISTRIB_RELEASE}
|
||||
if [ "$DISTNAME" != "ubuntu" ]; then
|
||||
if [ "$DISTNAME" = "linuxmint" ]; then
|
||||
if [ "$DISTVER" = "19" ] || [ "$DISTVER" = "19.1" ] || [ "$DISTVER" = "19.2" ]; then
|
||||
DISTNAME=ubuntu
|
||||
DISTVER=18.04
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
elif [ "$DISTNAME" = "elementary" ]; then
|
||||
if [ "$DISTVER" = "5.0" ]; then
|
||||
DISTNAME=ubuntu
|
||||
DISTVER=18.04
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
elif [ "$DISTVER" != "18.04" ] && [ "$DISTVER" != "18.10" ] && [ "$DISTVER" != "19.04" ] && [ "$DISTVER" != "19.10" ]; then
|
||||
resetDistVer
|
||||
fi
|
||||
fi
|
||||
IS_ARM=`(uname -a | grep aarch64) 1>/dev/null 2>&1 && echo 1`
|
||||
|
||||
if [ "$DISTNAME" = "unknown" ] && [ -f /etc/debian_version ]; then
|
||||
if [ -f /etc/solus-release ]; then
|
||||
DISTNAME=solus
|
||||
elif [ "$IS_ARM" = "1" ] && [ -f /etc/debian_version ]; then
|
||||
DISTNAME=debian
|
||||
DISTVER=$(head -1 /etc/debian_version|awk -F. '{print $1}')
|
||||
if [ "$DISTVER" != "9" ] && [ "$DISTVER" != "10" ]; then
|
||||
resetDistVer
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$DISTNAME" = "unknown" ]; then
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
if [ "$ID" = "arch" ]; then
|
||||
DISTNAME=arch
|
||||
elif [ "$ID" = "antergos" ] || [ "$ID" = "manjaro" ]; then
|
||||
DISTNAME=ubuntu
|
||||
DISTVER=18.10
|
||||
elif [ "$ID" = "opensuse-leap" ]; then
|
||||
if [ "$VERSION_ID" = "15.0" ]; then
|
||||
DISTNAME=opensuse
|
||||
DISTVER=15
|
||||
elif [ "$VERSION_ID" = "15.1" ]; then
|
||||
DISTNAME=opensuse
|
||||
DISTVER=15.1
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
elif [ "$ID" = "opensuse-tumbleweed" ]; then
|
||||
DISTNAME=tumbleweed
|
||||
DISTVER=
|
||||
if [ $(grep sid /etc/debian_version) ]; then
|
||||
DISTVER=10
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
else
|
||||
resetDistVer
|
||||
fi
|
||||
else
|
||||
DISTNAME=centos7
|
||||
DISTVER=
|
||||
fi
|
||||
|
||||
echo ${DISTNAME}${DISTVER}
|
||||
|
||||
@@ -33,8 +33,10 @@ const DependencyIPC = require('../src/renderer/ipc/DependencyIPC');
|
||||
const DownloadIPC = require('../src/renderer/ipc/DownloadIPC');
|
||||
const FilesystemIPC = require('../src/renderer/ipc/FilesystemIPC');
|
||||
const MountsIPC = require('../src/renderer/ipc/MountsIPC');
|
||||
const PinnedIPC = require('../src/renderer/ipc/PinnedIPC');
|
||||
const PlatformIPC = require('../src/renderer/ipc/PlatformIPC');
|
||||
const ReleaseIPC = require('../src/renderer/ipc/ReleaseIPC');
|
||||
const SkynetIPC = require('../src/renderer/ipc/SkynetIPC');
|
||||
const StateIPC = require('../src/renderer/ipc/StateIPC');
|
||||
const SystemIPC = require('../src/renderer/ipc/SystemIPC');
|
||||
const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC');
|
||||
@@ -199,7 +201,7 @@ const createWindow = () => {
|
||||
autoLauncher
|
||||
.isEnabled()
|
||||
.then((enabled) => {
|
||||
trayContextMenu.items[1].checked = enabled;
|
||||
trayContextMenu.items[2].checked = enabled;
|
||||
mainWindowTray.setToolTip('Repertory UI');
|
||||
mainWindowTray.setContextMenu(trayContextMenu)
|
||||
})
|
||||
@@ -308,16 +310,31 @@ if (!instanceLock) {
|
||||
configurePrimaryApp();
|
||||
}
|
||||
|
||||
const AppFunctions = {
|
||||
closeApplication,
|
||||
detectScript,
|
||||
dialog,
|
||||
getCleanupReleases: () => cleanupReleases,
|
||||
getMainWindow,
|
||||
saveUiSettings,
|
||||
setIsInstalling,
|
||||
setTrayImage,
|
||||
setWindowVisibility,
|
||||
standardIPCReply,
|
||||
unmountAllDrives: MountsIPC.unmountAllDrives,
|
||||
};
|
||||
|
||||
AppIPC.addListeners(ipcMain, closeApplication, setWindowVisibility);
|
||||
ConfigIPC.addListeners(ipcMain, standardIPCReply);
|
||||
DaemonIPC.addListeners(ipcMain, standardIPCReply);
|
||||
DependencyIPC.addListeners(ipcMain, standardIPCReply);
|
||||
DownloadIPC.addListeners(ipcMain, standardIPCReply);
|
||||
FilesystemIPC.addListeners(ipcMain, getMainWindow, dialog);
|
||||
MountsIPC.addListeners(ipcMain, setTrayImage, standardIPCReply);
|
||||
PlatformIPC.addListeners(ipcMain, detectScript, saveUiSettings);
|
||||
ReleaseIPC.addListeners(ipcMain, () => cleanupReleases, standardIPCReply);
|
||||
StateIPC.addListeners(ipcMain);
|
||||
SystemIPC.addListeners(ipcMain, closeApplication);
|
||||
UpgradeIPC.addListeners(ipcMain, setIsInstalling, MountsIPC.unmountAllDrives, standardIPCReply);
|
||||
AppIPC.addListeners(ipcMain, AppFunctions);
|
||||
ConfigIPC.addListeners(ipcMain, AppFunctions);
|
||||
DaemonIPC.addListeners(ipcMain, AppFunctions);
|
||||
DependencyIPC.addListeners(ipcMain, AppFunctions);
|
||||
DownloadIPC.addListeners(ipcMain, AppFunctions);
|
||||
FilesystemIPC.addListeners(ipcMain, AppFunctions);
|
||||
MountsIPC.addListeners(ipcMain, AppFunctions);
|
||||
PinnedIPC.addListeners(ipcMain, AppFunctions);
|
||||
PlatformIPC.addListeners(ipcMain, AppFunctions);
|
||||
ReleaseIPC.addListeners(ipcMain, AppFunctions);
|
||||
SkynetIPC.addListeners(ipcMain, AppFunctions);
|
||||
StateIPC.addListeners(ipcMain, AppFunctions);
|
||||
SystemIPC.addListeners(ipcMain, AppFunctions);
|
||||
UpgradeIPC.addListeners(ipcMain, AppFunctions);
|
||||
|
||||
261
releases.json
261
releases.json
@@ -1,259 +1,84 @@
|
||||
{
|
||||
"Locations": {
|
||||
"arch": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"centos7": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"1.3.2": {
|
||||
"sha256": "a8b9bff91d0f0685041737cfa40b4bc8ef765d24419104f3dd90f977023e7858",
|
||||
"sig": "CBEtKUDJwjPrchhNXvzulpfaBWFElDD+Kn9b14oF426IuKncsmqTWUesG3cT/beDk1onaDOhDIchyan6iW/BCdB+sVAuGAoRpm2lJ4lWba7ZPnXP7H10uIjVf1xASwIciP4jpAUPo0bO8hi9kqu1zkF9GeVHHb+SXlLhDP31wN7kHwgb/MeZ95/NqfucumYcwfFOZOnofSKaGQwHpbypy57YzyVCf/NzQDnYC46lcH9xVMmbgodT+y5MIIF0kkSJn0DbSfxLVMWTx1pnZUzJ7J827EKUgAS4d4uLI7ShskS+d84i8rNV9gXgqwYkqDvYbdFi7ArJZB7GJtyZ4VPTqRqGfxwF8rL3K0ml+635DfEXiHN8MbTuqwLHD2giChOfsdAAV4ngJ8xg7+Th8dqpgC92GXoOjNK7GSAyrOFsQ7xeqdVOBZMNk/dHVAb9eCTbPIsOjGFTAzOk8n3roM8naDUOq7PefUj1Jb2EGVhWnwS2ngvhNfRggZmmr3l9IElddaHro0JpnubsFmiAa+akV0P8VLb1jjpIJRfr2CIEnPTQbT4UQ1DdBitgkWiPbagTp8BQF1YGcVZ8jc9mbOxmF46z0MyuMQZjDHw2Vai0uPsoLIJHhy3I7nJqsnEw6gQX/l1VGj9wPHeaqMgGYGFykFfvVssh0CH45tUCdWL6Q9Hgx8+yLw0Lky/HIGMQc0qZUM9ute/9Buifd9cOK8+S5PaA0Zg0G/pIY6fThtO86wKJQnoPeiuBkJ9yXc20ZGrfb39Qnf2nZxrPyVl2iQJlven6CDBxc88henxzvxK61bXUynK8zHVuoIAC7ejWZEbPOqDV3DOOSVDz1+eq1MU2KZ4JmaLipzbdy/y+ph1TnCGwdL9R3s+xLYSaxA+ybhQVNXHhQ7+eZD6CWXytaIVYe8giL1ki8oN/18Bm1B2dVxCZ4KKbAToFpBOGvW+w8JFAC5nBK5aj2BGWEocyWQ1XIJmqVBd8RSOMYVYjQOhVnIvyJxOqvnrLUoRzJruj8R/riuCSMnfq17ykpjCzoNzIjvimCPyAib9kdWx7M8dyOS9Sls2sMLwJvDcsGi/FfuGqBmV2v28XagRZz70iypqNaIUrj4CShfz8e110su3BtLQHr1O6yffxy+whbB7w+ogZ6gz86AZ/63Zz6xbLIk4wuNx1UL/ZuRMGq4xOW/7zRpOYYSe/h98YFd9/UTcoi8Osjl+o1eVup6YWVeDvOG69uqwfID8XHivJnEV2NVFx8fbb6fHSrMg/QbjSngdyQ3b+5rWiZG01DGCDB5RFOvEKY24qrXXWQGAx+JXbj+/7xYWts+/Fv7hSqvpvf7H8aVCoOKXGtjJiy+K/9tAJJLQDAzo=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"centos8": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"darwin": {
|
||||
"1.1.4": {
|
||||
"sha256": "8c91046a83b689564355be3e0a3e194ab986c4633f112696e33e4bb7ebd7e46e",
|
||||
"sig": "BBds0xSaIqr2rns+cSr90bmxtfy2aIGNVcKgRfMYYqDntV5SkWxD8ivqPglFiLQA/jfgd5A8DjMJs4FA6FDvF1ZFcfgZLFw94OT1M3jPJ7cf5Izv1EQb+5AV7SK8H7DACDixcgKDDXO0WARmeqNXJZzyxkny8XKIiGDwLbmrXfFVuKPSLucbf6eaTem+xJHjQPzP9FH4Lwf9kbQxis2/PIT63yflQdM9CAAvfZUB4YUCOF5T8I43uNV2YDR0Z+k5FLTBexJDORDRixbrldWqNo7NQlNMR+26LY2IBppQqOMtDw9cpcfPj8bNgkWsZkFk/eOcrcpc8ENQodGxb01JahKJ20g9soJEAOhiFW3TOxzOLNq8cQFdKYgJv2K68lav/QzVPD6n14Bsr3+5mkX4fE1GmmN1HLgFO+JbD25qDrS4ExKR5TJ2Zdu136n/Mxbj2apYDhWtU8X6OD86F29m348XDonC9Fn+vzoE2M4xNKptMhukem7NFG7Z03v6+J73PaVM1NldARMGgVUYFA6QTn6Ns55YEXnsHoSoreHmgl5y3s8eRo0/oehv0cpPG6shCz71+YlVG/IgbKWIgYZ0jj03Cdsgv/XyadiwfCI9Q7uvl368Vlbuad4mqttMTZwHeZlKikTYb74QDyXqVoz0Q2ij7WET7MkXpA3++hDrPIN5Cb8qi/YuMp63w6TLfz4c/ZCQeje+0d7x6p63Z7TObEXDyBgdUAphX3T/u4pkEjXQKsfNRh8jaFOXU3z+hG8+IXjrZtLcDEzqICZ8JJCrkklgliTM1ECiekvJ9jwXTN2ClrzLQ3qhX8N+8jkvQ+yZxGxH+3rOX55RyW4OcMYUYvDpTtNHncVvABBlRvPGEGxtA7RMDTzOGe0lk70y07al6iFKDktaoYSFlcH+pUw7ifDZamefxlKDmtrMOMNmnrm+v8WH0g0MG8qy/nGPxv5dFIl9gAC/rvRElxX8FpA7y18i7fQ0IfhpaC58gZZADkF0nFOW15N7zwjS3qnhAGCBCBEDwlFG9brD71qjShtodgnz+GvrDvtqGTwNRllnZXSHPCWe33BOHy2c30LwE8vJq+ChVaJobCA9P8ipgZv/gAuZz7LNFc/Uong5TyKZk2uXun3XF1Qkw+ogBpl9aqMWIySZhkDnBcZjjE9pL/FQAkO1H90+zNnSdSAKZQaPckxLSyqHXxb4P7Da4nj3tad609NcxEzsz3dpEJTuYlqLqmO5kJmy29jNUHhQQdPCuKJtdm/coz3kWtC+uR6/SMnhviGnc7tQUqyb6HfGywKbmoAM93NHSGxNGyNMU+VJ20/YK1lmVFo3Pg/wq+aX35utcjQafpATqHeOeDw26fnZxBs=",
|
||||
"1.3.2": {
|
||||
"sha256": "d8e5749a43541edaeaa91cbc81c42fb21c8e4c8446c9c1dff0a8e3a0efc23270",
|
||||
"sig": "AKXLbLDnkBlB2b8dKLPZ5Vr9bcbv1dCvYGJTCltxYRJkOBzmQMTJ+ADKAtQQCfZKtNji+Dez28A401BMUY7xBd3Sh4FDnmNv64Q2zfqxQpOkhdrK8KcwK2aFCFbWn3vTnU3GBk2IXqGY8qIeR4d/v2UaBnnpK0EulztxPXojLz0pA6DVndJOodItoE6OUNohPd5MTWh5ewpjXJN+VKeao3ABkXgCzYLH/SSYCyi8ZFH8Ija20HJk8ST2QIokmRQGk5MDcyxylyWkusGOf7yn9M6vdtHNfrHfWXGxnMTuaw9+YNTuxZNDgIFpmoVx4t7HSoNti+ZZwyo+1jtDeh0xPmdEGpnZOY7zp94zlh4zP9Cx5cYg6W5/NQKQzDtlUZlELqR8Kwoa/bDCwWeSkifGqEJT2Yezs9r5llSoIh8PrxLkmdaX2vkOvaRYYE0kvdO2MllT8SSyYkum/9eD8uA52IYbUliJqfAf43gOj9n0ooRcCo45lBY5PIUB44XBLEGK15OuarybSyaBDMFbc3HWh1A9jB9kSPctnMMJMNMsocTI5AEN8eRbLyF7z01WE+nB7JDS8NW3+QRFyQInxdXpcGeKO6riuSO7aXHLWAZvUVSxThSuV7JtsugtUnIsP9ah7njtcVHOSEcUG3U2WxZRbe6PG5pdjS4Px+E9Yx35zLSwo39TcwLb2fM7mQKREsH0fUrRd4WN1T0pWOuzDlWSq4pfDXWdO8y828l8XVJzw5LUH9U0F7qAiuzEVQIYJ+xjjAp4JVJXBof4iWeW4N+7PYgntEgr94Ti6rjb6BrKWdl4B+9j79EkC2yLWafcGnryUaSfOFDFiB8Upf6NQ+a1TxY5ufFe1JfuI58rchUZi4SjNdyFMat4GSdTI6I513ZVcohBb1KVIxEM0u38hJqKIFx3stWMZrLvyFH9ceqzXzVQCIDE31usUKFsXallWMQyB2kIy7qoif7zmDhdJQZboD7ZHsTrcSK1u9D+h3oG1poN+AULmhgYZNT11ktRiVUbmzDS0qA7lnF8dcsuthj0vHP0/BFN6a7ljQuwDVY1xoeCf+RobfNWucK2MLxmqV1z9nO4jpdy/GfC/5ln4Tev+5luo5ux0BXXXAWuzGyQ0QK+13k3lIUwxsDNou0dMDHJPaMavBjmlm0Uf5esZdSIU9U2sdfuO+j/6enwGvESlrKmBCsdPatHREP931dGPZCsF1m30OylV+m7WhBh9w6AbklmjHNCZu3C2Ln23gOKGX8HpQ9nnaPCicmlfSyBjPoKVtDCACVbjwRfTJ+UXCocrCVSbAhskOSvlq050QFQcZO5DzFWdeTfG6x6MrqdSelYThjy5xn8AXuqsFSLIdYpCAQ=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/68WJfKsz",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_mac.dmg"
|
||||
]
|
||||
}
|
||||
},
|
||||
"debian9": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"debian10": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fedora28": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fedora29": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fedora30": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fedora31": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"opensuse15": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"opensuse15.1": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_mac.dmg"
|
||||
]
|
||||
}
|
||||
},
|
||||
"solus": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"1.3.2": {
|
||||
"sha256": "a8b9bff91d0f0685041737cfa40b4bc8ef765d24419104f3dd90f977023e7858",
|
||||
"sig": "CBEtKUDJwjPrchhNXvzulpfaBWFElDD+Kn9b14oF426IuKncsmqTWUesG3cT/beDk1onaDOhDIchyan6iW/BCdB+sVAuGAoRpm2lJ4lWba7ZPnXP7H10uIjVf1xASwIciP4jpAUPo0bO8hi9kqu1zkF9GeVHHb+SXlLhDP31wN7kHwgb/MeZ95/NqfucumYcwfFOZOnofSKaGQwHpbypy57YzyVCf/NzQDnYC46lcH9xVMmbgodT+y5MIIF0kkSJn0DbSfxLVMWTx1pnZUzJ7J827EKUgAS4d4uLI7ShskS+d84i8rNV9gXgqwYkqDvYbdFi7ArJZB7GJtyZ4VPTqRqGfxwF8rL3K0ml+635DfEXiHN8MbTuqwLHD2giChOfsdAAV4ngJ8xg7+Th8dqpgC92GXoOjNK7GSAyrOFsQ7xeqdVOBZMNk/dHVAb9eCTbPIsOjGFTAzOk8n3roM8naDUOq7PefUj1Jb2EGVhWnwS2ngvhNfRggZmmr3l9IElddaHro0JpnubsFmiAa+akV0P8VLb1jjpIJRfr2CIEnPTQbT4UQ1DdBitgkWiPbagTp8BQF1YGcVZ8jc9mbOxmF46z0MyuMQZjDHw2Vai0uPsoLIJHhy3I7nJqsnEw6gQX/l1VGj9wPHeaqMgGYGFykFfvVssh0CH45tUCdWL6Q9Hgx8+yLw0Lky/HIGMQc0qZUM9ute/9Buifd9cOK8+S5PaA0Zg0G/pIY6fThtO86wKJQnoPeiuBkJ9yXc20ZGrfb39Qnf2nZxrPyVl2iQJlven6CDBxc88henxzvxK61bXUynK8zHVuoIAC7ejWZEbPOqDV3DOOSVDz1+eq1MU2KZ4JmaLipzbdy/y+ph1TnCGwdL9R3s+xLYSaxA+ybhQVNXHhQ7+eZD6CWXytaIVYe8giL1ki8oN/18Bm1B2dVxCZ4KKbAToFpBOGvW+w8JFAC5nBK5aj2BGWEocyWQ1XIJmqVBd8RSOMYVYjQOhVnIvyJxOqvnrLUoRzJruj8R/riuCSMnfq17ykpjCzoNzIjvimCPyAib9kdWx7M8dyOS9Sls2sMLwJvDcsGi/FfuGqBmV2v28XagRZz70iypqNaIUrj4CShfz8e110su3BtLQHr1O6yffxy+whbB7w+ogZ6gz86AZ/63Zz6xbLIk4wuNx1UL/ZuRMGq4xOW/7zRpOYYSe/h98YFd9/UTcoi8Osjl+o1eVup6YWVeDvOG69uqwfID8XHivJnEV2NVFx8fbb6fHSrMg/QbjSngdyQ3b+5rWiZG01DGCDB5RFOvEKY24qrXXWQGAx+JXbj+/7xYWts+/Fv7hSqvpvf7H8aVCoOKXGtjJiy+K/9tAJJLQDAzo=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tumbleweed": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ubuntu18.04": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ubuntu18.10": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ubuntu19.04": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ubuntu19.10": {
|
||||
"1.1.4": {
|
||||
"sha256": "85347471f33dd41a35eb296cda03590d451322cd54a077cef470ccbfe52a141b",
|
||||
"sig": "CGialiJMT6nETdGgP3vQvsXSWQYUEX7j7Cck0NSiW0cE4A1kW3mabM2I/5Yt4NwkDqBIAXofPyUd1JQ4UrF8cijfLNv3ECSaDJs7bWrISDr3Z8ti0uZnN3RUdJDCYDvAWBVqGC82Q1fmLkYJhd33o2B9DzjmvO+i7+buNyDB3dBIY5Fa3rnLKsIY7WB5EASBG3TzDlsZOtLvNgKo/3fLJP2RFyczSVCFZeHlppJbyXCakj50eCNbxjP6SLaD3oE+M0xGvyPyKTk0OGoJTGKM13q30WKbM6cMfL87BbUueGkBFDZeNMWU8Mz/fdGG21WqNN1p7k3CilJJQz7ZEE9bBhlsIX5j5rvL2qNYJoQbJQbxVjyoWHOOlqA62WUTOZhyiHD3OWQ58Fd9JqGDiIhg+ChIBWzyEXNdE6GQr7/qEiAQDbEsU06WOzfj3uxAV//iy/noalpyhlnr0TUu5xKSnoR7jQhCtvOdlrmgs8y+mTEpwDjXCR0S7jDxp+cEbVHb57Rl8N5thVrOs5RQfshhcqwBR/scCM9iCr0v3D7uyGMinTBT3A8lwerN8iO17FhtEerovn4Zhg2p7quZRiUcYLdx4EOlIMWuORyDk3s0S4VZ27t92NYwMlwMA1TbUQ/XuN1oZaQQ9ipSDjNDDVZto3Q4vwlqL3g1TIs1JyxfHkwUTpuFjg3ZqY+uRNxeJTDlj9JHXiAIZ+VeBggB7EixKE+wMi1A6a+Si/ZpBbroK5gP1gODShXYUN4eGmodxqF2/gVrJcimEeHkyBZ74dZIa9gTSIOxeo1OS70fFU+hQy9/V0/q4PElPZn+EzLTGknWh1EDmqReZ1svv0tYf0Ve/l5szhdNGsb2LUEZ7G/r0Bs7fyQjA5YZI+byOPTSA1Q9vlT63sodpBRDURWkqTRFRPWlaEY2kMZu/vJ422PSjK51X4EPHWmpOkDkxFzx2WAs2rKZ9QXCkU0RGu0Ctial/zNVhm20eVklcuIirEnmUK9Kod0KBmjl+U6U6jHvlhOEC4zp2QcDsrLSQu7gDHvJWbYmaKBTiXrEvk52G/EN9aD86Am5k0bwPU5ktj8z38+5sxVns1R4e9J1WaZgD2dtFvpSxJbjVf09AZHIeyNw/BRu3GfJU6dX6KJVMCMtN+R5xv/ZckylI355R4nrf+7PLNsGg2tqlemvHiwdQkOb3/TOLrJpO94JRjm9k16huysztkpgJIGyKvHXT6z1A3cwCoJzrK7sQ1awedPvO+N8UGJ7kXHRgLLhQ2yWfZfIsD/s0Ji5GFrItm1nlqHAd/ZFh82jNKmvh6+n42l5TimH9Q0Cf+7bK5Y7SVYzhSAW2B8ivHsPMpAkLJ6d17ZMxtrr4/Q=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/KfWzWfxp",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_linux_x86_64.AppImage"
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"win32": {
|
||||
"1.1.4": {
|
||||
"sha256": "c942d2779e7da597605d9fb8fa2876f9c82e5255ee859dac47d209e64f6d1707",
|
||||
"sig": "AhYYFZxYLVibiRvc0PyOSnU+CWoaVwWvKfMWibdYsxINuwPUMVRjvXe2dPB8ghqe3qODw4XswZnwu7BwqTbCwaEDNdj8f4kpmd0cNnFTP4jP9q0qKcM4thOq1rCr8VuptS8UsTT0UXVqExf2K06efyrG1GYjeTG9LJpCJ5YFiet2vrarqHap/FiRjgVhRcE6mkoDPLj+eu9z9AFPQ4uNrMazFgHj7Vbq4H+oEIwMiuC6umbd3xGBSvlmudp3OONYrybwIuMpqaUPv874eFQOfrlYVPadAr9vn6QxFEjl1rBkd9xQNte/CBYNe1/fD5p0iLjJOAqz24cgZCMpKsRUOpgJcwf7NVU819Ymbu9/zVtBJ2F/T2wg2o8wTyymWqTdMSEDZu0fUb3StHzcrEqFzAHckyDF6d6qrKBXc8bzs0T85FQUZUqzoZng3b+Y2xtSoaiJA/6Es+Qx6t1T7dXqtN7xu43ZRrNNLcLG/FtHx8X7UuAzNt4FL5kbPM3EMtK1J3gsDpTQSsLKPKTWZXuEfUOCI/b1psL3YcsQv9Ta1o2I9bGqYTJtsGTvCkXG20KoqcqXiYFyGWTnztd8jcPhvxjI9aI0sblOTMN4lBmkNroOkSu39/+z8UvsKgioE1FNmjr5edtqbXdJ2C/6PZITKe3Hl+sfJDHinn3QhYp/Hl9gzL64KD2lJRiM7rCWYfyUvMnwewG5aQvkE/Jf9t05DYCX9rwbUoz8QSV/HBGOXrrDpo+tJeZ74FbaNVlW4IApnpSdQpVqwdJCf7rdPwOm13DvkgSDjlz02BdLKk93X0fX4zCGnF/RNYw4meLkbs0UmrDml9hCfEHRjrYJe0i9GqRwyBH9kp+sbBTYcomZgro24pEQRXi6DoK5xjpu8K+ywAAjWFn7vrZQeuNTmw5Itib2xQQnFzbg/u35KFXvaZ6c0MBi+rIwUQTqOO1l6U6PN5+U7imRxDKvy6y1GsusJGXee4HATJiZc/5PCRK42A2YJ1NBHDJOm0aLcx8oboYtmvGOIObAcpEIkfrDdykxBvpxFPBW6AXacj5vwi0Io+1TjB7aXmyRiOGV/nzerUdFTtnbkvgOLIGiOy+B3NN6b8WZVkbubcaWBqW0yuHwVcRJtsbhtiLhGXc4n3iFPR+1gJCHd0rUKF2HKs3MALk4Gh/V1rZqY7iklceEJkMgCHwKZ56vIYiNkwT5uco0UNEpOAedAfRMBAvs1WQuyltdYlDL/9XwPfc6Wg7BHL1wfmq/wFaE4kKtbEXM+Esh9GmLXPcsNrDU71hkhTTl0edmXlp4f8olJjJxas0YerwyVvhIDlG9RSV0PewYIWnLevb9kA9xAGmFbgp4yfyWAqC2ORY=",
|
||||
"1.3.1": {
|
||||
"sig": "29b91fcba36fc7cac38837674559c6af1169b3dc54bd57284c3ecf44a1af869d",
|
||||
"sha256":"BLy7sutkbfkK39sOPPlDdn8DVoorVC7NiEl7LvCqp9Dcnt+6afmszRLFYNu0ipu2krN8HdA2uCqCS2lAlNwOzGzkCGN6RHfobHRqj/HObNrWsvGKSp+Vdt96J76TmrV/gjPGLJOBq5dobBGmN1KJSrkLpWj6IOoEuR4/7WkSJah4XWMIfVlPAZKwP6yNv6OaYqjIy74geKkoCI5/mmwDzUX/T8M3DsdSbm3kuYJ0S3E3mEO2lGH++nxDDPMdA2yjFbBwHU2RFQShbL7jB/AqodSrIU4wOstbRWiF34KUZ/KYG+ZFvwEBshbgDoZQyNbVmFDD86g/sEatnQRpN7juGBWwScgPfoTbdl9y0lf5U85dicOe7+iholAUVprLuPk9Y3NVXoBFM9g9mkqgMVoYoSUgh/BJBnomDgvt1pd4Id/CO26buhSU28fRbQ/c8i+x5zdsfqN7p/64sj/2Xp0HQMwFmc3IotjDEtUTcfI+VGP62pJLMao5vheZ8JU05GXn7ZA7Sojfov9luuoajguukXHEclaxgWKyo0u/ZcowExrKpkegxui6v/3+W/pTv/cSg01CFyJTMTvT9cT7O0bWSdUcxOgpYptV5wVOdglUm/aQscqtDVaVv90ZMNldQ2YVI6wuFTIcUpWNCa/r5oIcGVWWrxBu9uq0jLF3AoTNpLeLJuJL37KacJwNqNDUolG6bwdjvAI1cpcy7fGrzbGE95pPmHpruVfKpLpSQ7tKnT5dV9H2gQz63gl/QzvVGnov0pLUNuJwQQhdvcGZXnVcB96u0i8GBacr4WZVhhJWGWk/u5zx5ganXcXYkodDLN4zpI/aV/TdW2QH2xN8OOmQOZ8yZS9toS0vcIUeoT6eQUg5clJniKtivUv2GSldGzKG9xk3s/2IQlivOX2BxnTSYaP1AkWTUSyJeXcEYbYytLiFrMj875NxeYfKD74SGfxWh//NWyzL7GG/q8G77xw/1s4LorzhSN5jifyPgtSWI4h3WaSu6nfLJHioujAEMDwhkwP9mPmPmBUbTveKWdTZy7YQGGN+4DMJLoGQnx0qBbsIOWfmt+/cjQ72Sx8V7oRzElSohxg9q/oCKkMo4f1JuS1kOGasbPnXjjjuWTX1bOeQ86TjBi+wSt1Y5VgifvyfEdEeDvvwrcm2Cp3NnWYAXhqE5tgVUIYLEtsbHaAtWOv4XSlp8EISd1XIDeTADHNonEEWUWGjNJvylQ05iGUjQny6CObSN3gNbwL0xO/gI1o1+SR4HsM5u2leIKaD5hU/6OdsorVX53b1xPNLyLrDr1uMgfM6lGgCyrs8DY5hVIujIycTSEbaAGpMA2+ovzpa9jO/cZ0/slMMf55Roic2yCQ=",
|
||||
"urls": [
|
||||
"https://pixeldrain.com/api/file/h5HoAqxF",
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.4_win.exe"
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_win.exe"
|
||||
]
|
||||
}
|
||||
},
|
||||
"debian9": {
|
||||
"1.3.2": {
|
||||
"sig": "CBEtKUDJwjPrchhNXvzulpfaBWFElDD+Kn9b14oF426IuKncsmqTWUesG3cT/beDk1onaDOhDIchyan6iW/BCdB+sVAuGAoRpm2lJ4lWba7ZPnXP7H10uIjVf1xASwIciP4jpAUPo0bO8hi9kqu1zkF9GeVHHb+SXlLhDP31wN7kHwgb/MeZ95/NqfucumYcwfFOZOnofSKaGQwHpbypy57YzyVCf/NzQDnYC46lcH9xVMmbgodT+y5MIIF0kkSJn0DbSfxLVMWTx1pnZUzJ7J827EKUgAS4d4uLI7ShskS+d84i8rNV9gXgqwYkqDvYbdFi7ArJZB7GJtyZ4VPTqRqGfxwF8rL3K0ml+635DfEXiHN8MbTuqwLHD2giChOfsdAAV4ngJ8xg7+Th8dqpgC92GXoOjNK7GSAyrOFsQ7xeqdVOBZMNk/dHVAb9eCTbPIsOjGFTAzOk8n3roM8naDUOq7PefUj1Jb2EGVhWnwS2ngvhNfRggZmmr3l9IElddaHro0JpnubsFmiAa+akV0P8VLb1jjpIJRfr2CIEnPTQbT4UQ1DdBitgkWiPbagTp8BQF1YGcVZ8jc9mbOxmF46z0MyuMQZjDHw2Vai0uPsoLIJHhy3I7nJqsnEw6gQX/l1VGj9wPHeaqMgGYGFykFfvVssh0CH45tUCdWL6Q9Hgx8+yLw0Lky/HIGMQc0qZUM9ute/9Buifd9cOK8+S5PaA0Zg0G/pIY6fThtO86wKJQnoPeiuBkJ9yXc20ZGrfb39Qnf2nZxrPyVl2iQJlven6CDBxc88henxzvxK61bXUynK8zHVuoIAC7ejWZEbPOqDV3DOOSVDz1+eq1MU2KZ4JmaLipzbdy/y+ph1TnCGwdL9R3s+xLYSaxA+ybhQVNXHhQ7+eZD6CWXytaIVYe8giL1ki8oN/18Bm1B2dVxCZ4KKbAToFpBOGvW+w8JFAC5nBK5aj2BGWEocyWQ1XIJmqVBd8RSOMYVYjQOhVnIvyJxOqvnrLUoRzJruj8R/riuCSMnfq17ykpjCzoNzIjvimCPyAib9kdWx7M8dyOS9Sls2sMLwJvDcsGi/FfuGqBmV2v28XagRZz70iypqNaIUrj4CShfz8e110su3BtLQHr1O6yffxy+whbB7w+ogZ6gz86AZ/63Zz6xbLIk4wuNx1UL/ZuRMGq4xOW/7zRpOYYSe/h98YFd9/UTcoi8Osjl+o1eVup6YWVeDvOG69uqwfID8XHivJnEV2NVFx8fbb6fHSrMg/QbjSngdyQ3b+5rWiZG01DGCDB5RFOvEKY24qrXXWQGAx+JXbj+/7xYWts+/Fv7hSqvpvf7H8aVCoOKXGtjJiy+K/9tAJJLQDAzo=",
|
||||
"sha256": "a8b9bff91d0f0685041737cfa40b4bc8ef765d24419104f3dd90f977023e7858",
|
||||
"urls": [
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"debian10": {
|
||||
"1.3.2": {
|
||||
"sig": "CBEtKUDJwjPrchhNXvzulpfaBWFElDD+Kn9b14oF426IuKncsmqTWUesG3cT/beDk1onaDOhDIchyan6iW/BCdB+sVAuGAoRpm2lJ4lWba7ZPnXP7H10uIjVf1xASwIciP4jpAUPo0bO8hi9kqu1zkF9GeVHHb+SXlLhDP31wN7kHwgb/MeZ95/NqfucumYcwfFOZOnofSKaGQwHpbypy57YzyVCf/NzQDnYC46lcH9xVMmbgodT+y5MIIF0kkSJn0DbSfxLVMWTx1pnZUzJ7J827EKUgAS4d4uLI7ShskS+d84i8rNV9gXgqwYkqDvYbdFi7ArJZB7GJtyZ4VPTqRqGfxwF8rL3K0ml+635DfEXiHN8MbTuqwLHD2giChOfsdAAV4ngJ8xg7+Th8dqpgC92GXoOjNK7GSAyrOFsQ7xeqdVOBZMNk/dHVAb9eCTbPIsOjGFTAzOk8n3roM8naDUOq7PefUj1Jb2EGVhWnwS2ngvhNfRggZmmr3l9IElddaHro0JpnubsFmiAa+akV0P8VLb1jjpIJRfr2CIEnPTQbT4UQ1DdBitgkWiPbagTp8BQF1YGcVZ8jc9mbOxmF46z0MyuMQZjDHw2Vai0uPsoLIJHhy3I7nJqsnEw6gQX/l1VGj9wPHeaqMgGYGFykFfvVssh0CH45tUCdWL6Q9Hgx8+yLw0Lky/HIGMQc0qZUM9ute/9Buifd9cOK8+S5PaA0Zg0G/pIY6fThtO86wKJQnoPeiuBkJ9yXc20ZGrfb39Qnf2nZxrPyVl2iQJlven6CDBxc88henxzvxK61bXUynK8zHVuoIAC7ejWZEbPOqDV3DOOSVDz1+eq1MU2KZ4JmaLipzbdy/y+ph1TnCGwdL9R3s+xLYSaxA+ybhQVNXHhQ7+eZD6CWXytaIVYe8giL1ki8oN/18Bm1B2dVxCZ4KKbAToFpBOGvW+w8JFAC5nBK5aj2BGWEocyWQ1XIJmqVBd8RSOMYVYjQOhVnIvyJxOqvnrLUoRzJruj8R/riuCSMnfq17ykpjCzoNzIjvimCPyAib9kdWx7M8dyOS9Sls2sMLwJvDcsGi/FfuGqBmV2v28XagRZz70iypqNaIUrj4CShfz8e110su3BtLQHr1O6yffxy+whbB7w+ogZ6gz86AZ/63Zz6xbLIk4wuNx1UL/ZuRMGq4xOW/7zRpOYYSe/h98YFd9/UTcoi8Osjl+o1eVup6YWVeDvOG69uqwfID8XHivJnEV2NVFx8fbb6fHSrMg/QbjSngdyQ3b+5rWiZG01DGCDB5RFOvEKY24qrXXWQGAx+JXbj+/7xYWts+/Fv7hSqvpvf7H8aVCoOKXGtjJiy+K/9tAJJLQDAzo=",
|
||||
"sha256": "a8b9bff91d0f0685041737cfa40b4bc8ef765d24419104f3dd90f977023e7858",
|
||||
"urls": [
|
||||
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.3.2_linux_x86_64.AppImage"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Versions": {
|
||||
"arch": [
|
||||
"1.1.4"
|
||||
],
|
||||
"centos7": [
|
||||
"1.1.4"
|
||||
"1.3.2"
|
||||
],
|
||||
"darwin": [
|
||||
"1.1.4"
|
||||
],
|
||||
"debian9": [
|
||||
"1.1.4"
|
||||
],
|
||||
"debian10": [
|
||||
"1.1.4"
|
||||
],
|
||||
"fedora28": [
|
||||
"1.1.4"
|
||||
],
|
||||
"fedora29": [
|
||||
"1.1.4"
|
||||
],
|
||||
"fedora30": [
|
||||
"1.1.4"
|
||||
],
|
||||
"fedora31": [
|
||||
"1.1.4"
|
||||
"1.3.2"
|
||||
],
|
||||
"linux": [
|
||||
"unavailable"
|
||||
],
|
||||
"opensuse15": [
|
||||
"1.1.4"
|
||||
],
|
||||
"opensuse15.1": [
|
||||
"1.1.4"
|
||||
],
|
||||
"solus": [
|
||||
"1.1.4"
|
||||
],
|
||||
"tumbleweed": [
|
||||
"1.1.4"
|
||||
],
|
||||
"ubuntu18.04": [
|
||||
"1.1.4"
|
||||
],
|
||||
"ubuntu18.10": [
|
||||
"1.1.4"
|
||||
],
|
||||
"ubuntu19.04": [
|
||||
"1.1.4"
|
||||
],
|
||||
"ubuntu19.10": [
|
||||
"1.1.4"
|
||||
"1.3.2"
|
||||
],
|
||||
"unknown": [
|
||||
"unavailable"
|
||||
],
|
||||
"win32": [
|
||||
"1.1.4"
|
||||
"1.3.2"
|
||||
],
|
||||
"centos8": [
|
||||
"1.1.4"
|
||||
"debian9": [
|
||||
"1.3.2"
|
||||
],
|
||||
"debian10": [
|
||||
"1.3.2"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,13 @@ import InfoDetails from './components/InfoDetails/InfoDetails';
|
||||
import IPCContainer from './containers/IPCContainer/IPCContainer';
|
||||
import Loading from './components/UI/Loading/Loading';
|
||||
import MountItems from './containers/MountItems/MountItems';
|
||||
import NewReleases from './components/NewReleases/NewReleases';
|
||||
import {notifyError} from './redux/actions/error_actions';
|
||||
import Reboot from './components/Reboot/Reboot';
|
||||
import {
|
||||
setDismissNewReleasesAvailable,
|
||||
setNewReleasesAvailable
|
||||
} from './redux/actions/release_version_actions';
|
||||
import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay';
|
||||
import {
|
||||
displaySelectAppPlatform,
|
||||
@@ -28,9 +33,13 @@ import {
|
||||
} from './redux/actions/release_version_actions';
|
||||
import YesNo from './components/YesNo/YesNo';
|
||||
import {createModalConditionally} from './utils';
|
||||
import SkynetImport from './containers/SkynetImport/SkynetImport';
|
||||
import ApplicationBusy from './components/ApplicationBusy/ApplicationBusy';
|
||||
import SkynetExport from './containers/SkynetExport/SkynetExport';
|
||||
import PinnedManager from './containers/PinnedManager/PinnedManager';
|
||||
|
||||
const Constants = require('./constants');
|
||||
const Scheduler = require('node-schedule');
|
||||
const Scheduler = require('node-cron');
|
||||
|
||||
class App extends IPCContainer {
|
||||
componentDidMount() {
|
||||
@@ -46,7 +55,7 @@ class App extends IPCContainer {
|
||||
}
|
||||
};
|
||||
detectUpgrades();
|
||||
this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', detectUpgrades);
|
||||
this.scheduledUpdateJob = Scheduler.schedule('23 11 * * *', detectUpgrades);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -54,15 +63,15 @@ class App extends IPCContainer {
|
||||
(prevProps.ReleaseVersion !== this.props.ReleaseVersion) ||
|
||||
(prevProps.VersionLookup !== this.props.VersionLookup)) {
|
||||
this.props.saveState();
|
||||
} else if (Object.keys(this.props.ProviderState).filter(k=> {
|
||||
return this.props.ProviderState[k] !== prevProps.ProviderState[k];
|
||||
}).length > 0) {
|
||||
} else if (Object.keys(this.props.ProviderState).filter(k => {
|
||||
return this.props.ProviderState[k] !== prevProps.ProviderState[k];
|
||||
}).length > 0) {
|
||||
this.props.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Scheduler.cancelJob(this.scheduledUpdateJob);
|
||||
this.scheduledUpdateJob.stop();
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
|
||||
@@ -72,6 +81,15 @@ class App extends IPCContainer {
|
||||
this.props.VersionLookup[Constants.RELEASE_TYPES[this.props.Release]][this.props.ReleaseVersion];
|
||||
};
|
||||
|
||||
handleUpgradeIconClicked = () => {
|
||||
if (this.props.UpgradeAvailable) {
|
||||
this.props.setDismissUIUpgrade(false)
|
||||
} else if (this.props.NewReleasesAvailable2.length > 0) {
|
||||
this.props.setNewReleasesAvailable(this.props.NewReleasesAvailable2);
|
||||
this.props.setDismissNewReleasesAvailable(false);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const selectedVersion = this.getSelectedVersion();
|
||||
|
||||
@@ -89,13 +107,30 @@ class App extends IPCContainer {
|
||||
!missingDependencies &&
|
||||
!this.props.InstallActive;
|
||||
|
||||
const remoteSupported = this.props.LocationsLookup[selectedVersion] &&
|
||||
const remoteSupported = this.props.LocationsLookup[selectedVersion] &&
|
||||
this.props.LocationsLookup[selectedVersion].supports_remote;
|
||||
|
||||
const s3Supported = this.props.LocationsLookup[selectedVersion] &&
|
||||
this.props.LocationsLookup[selectedVersion].s3_support;
|
||||
|
||||
const skynetSupported = this.props.LocationsLookup[selectedVersion] &&
|
||||
this.props.LocationsLookup[selectedVersion].skynet_support;
|
||||
|
||||
const scPrimeSupported = this.props.LocationsLookup[selectedVersion] &&
|
||||
this.props.LocationsLookup[selectedVersion].siaprime_support;
|
||||
|
||||
const siaSupported = this.props.LocationsLookup[selectedVersion] &&
|
||||
this.props.LocationsLookup[selectedVersion].sia_support;
|
||||
|
||||
const showConfig = !missingDependencies &&
|
||||
!this.props.DisplayPinnedManager &&
|
||||
this.props.DisplayConfiguration &&
|
||||
!this.props.RebootRequired;
|
||||
|
||||
const showPinnedManager = !missingDependencies &&
|
||||
!this.props.RebootRequired &&
|
||||
this.props.DisplayPinnedManager;
|
||||
|
||||
const showUpgrade = this.props.UpgradeAvailable &&
|
||||
!this.props.DisplayError &&
|
||||
!showConfig &&
|
||||
@@ -111,15 +146,61 @@ class App extends IPCContainer {
|
||||
!this.props.DismissDependencies &&
|
||||
this.props.AllowMount;
|
||||
|
||||
const configDisplay = createModalConditionally(showConfig, <Configuration version={selectedVersion} remoteSupported={remoteSupported} />);
|
||||
const showNewReleases = !showConfig &&
|
||||
!this.props.DisplayConfirmYesNo &&
|
||||
!showDependencies &&
|
||||
!this.props.DownloadActive &&
|
||||
!this.props.DisplayError &&
|
||||
!this.props.DisplayInfo &&
|
||||
!this.props.InstallActive &&
|
||||
!this.props.RebootRequired &&
|
||||
!this.props.DisplaySelectAppPlatform &&
|
||||
!showUpgrade &&
|
||||
!this.props.DismissNewReleasesAvailable &&
|
||||
(this.props.NewReleasesAvailable.length > 0);
|
||||
|
||||
const showSkynetImport = !showConfig &&
|
||||
!showDependencies &&
|
||||
!this.props.DownloadActive &&
|
||||
!showNewReleases &&
|
||||
!this.props.RebootRequired &&
|
||||
!this.props.DisplaySelectAppPlatform &&
|
||||
!showUpgrade &&
|
||||
this.props.DisplayImport;
|
||||
|
||||
const showSkynetExport = !showConfig &&
|
||||
!showDependencies &&
|
||||
!this.props.DownloadActive &&
|
||||
!showNewReleases &&
|
||||
!this.props.RebootRequired &&
|
||||
!this.props.DisplaySelectAppPlatform &&
|
||||
!showUpgrade &&
|
||||
this.props.DisplayExport;
|
||||
|
||||
const configDisplay = createModalConditionally(showConfig, <Configuration
|
||||
version={selectedVersion}
|
||||
s3Supported={s3Supported}
|
||||
remoteSupported={remoteSupported}/>);
|
||||
const pinnedManagerDisplay = createModalConditionally(showPinnedManager, <PinnedManager
|
||||
version={selectedVersion}/>)
|
||||
const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, <YesNo/>);
|
||||
const dependencyDisplay = createModalConditionally(showDependencies, <DependencyList/>);
|
||||
const downloadDisplay = createModalConditionally(this.props.DownloadActive, <DownloadProgress/>);
|
||||
const dependencyDisplay = createModalConditionally(showDependencies,
|
||||
<DependencyList/>, false, this.props.InstallActive);
|
||||
const downloadDisplay = createModalConditionally(this.props.DownloadActive,
|
||||
<DownloadProgress/>, false, true);
|
||||
const errorDisplay = createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
|
||||
const infoDisplay = createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true);
|
||||
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot />);
|
||||
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, <SelectAppPlatform/>);
|
||||
const newReleasesDisplay = createModalConditionally(showNewReleases, <NewReleases/>);
|
||||
const rebootDisplay = createModalConditionally(this.props.RebootRequired, <Reboot/>);
|
||||
const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform,
|
||||
<SelectAppPlatform/>);
|
||||
const upgradeDisplay = createModalConditionally(showUpgrade, <UpgradeUI/>);
|
||||
const importDisplay = createModalConditionally(showSkynetImport, <SkynetImport
|
||||
version={selectedVersion}/>);
|
||||
const exportDisplay = createModalConditionally(showSkynetExport, <SkynetExport
|
||||
version={selectedVersion}/>)
|
||||
const appBusyDisplay = createModalConditionally(this.props.AppBusy,
|
||||
<ApplicationBusy/>, false, true, this.props.AppBusyTransparent);
|
||||
|
||||
let mainContent = [];
|
||||
if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
|
||||
@@ -142,7 +223,18 @@ class App extends IPCContainer {
|
||||
mainContent.push((
|
||||
<Box dxStyle={{padding: 'var(--default_spacing)', height: 'auto'}}
|
||||
key={'md_' + key++}>
|
||||
<MountItems remoteSupported={remoteSupported}/>
|
||||
<MountItems s3Supported={s3Supported}
|
||||
remoteSupported={remoteSupported}
|
||||
scPrimeSupported={scPrimeSupported}
|
||||
siaSupported={siaSupported}
|
||||
skynetSupported={skynetSupported}/>
|
||||
</Box>
|
||||
));
|
||||
} else if (selectedVersion !== 'unavailable') {
|
||||
mainContent.push((
|
||||
<Box dxStyle={{padding: 'var(--default_spacing)', height: '170px'}}
|
||||
key={'md_' + key++}>
|
||||
<Loading/>
|
||||
</Box>
|
||||
));
|
||||
}
|
||||
@@ -162,12 +254,13 @@ class App extends IPCContainer {
|
||||
textAlign={'center'}
|
||||
type={'Heading1'}/>
|
||||
<UpgradeIcon
|
||||
available={this.props.UpgradeAvailable}
|
||||
clicked={()=>this.props.setDismissUIUpgrade(false)}
|
||||
available={this.props.UpgradeAvailable || (this.props.NewReleasesAvailable2.length > 0)}
|
||||
newReleases={!this.props.UpgradeAvailable && (this.props.NewReleasesAvailable2.length > 0)}
|
||||
clicked={this.handleUpgradeIconClicked}
|
||||
col={dimensions => dimensions.columns - 6}
|
||||
colSpan={5}
|
||||
row={1}
|
||||
rowSpan={remain=>remain - 1}/>
|
||||
rowSpan={remain => remain - 1}/>
|
||||
</Grid>
|
||||
</Box>
|
||||
</div>
|
||||
@@ -175,14 +268,19 @@ class App extends IPCContainer {
|
||||
{mainContent}
|
||||
</div>
|
||||
</div>
|
||||
{importDisplay}
|
||||
{exportDisplay}
|
||||
{newReleasesDisplay}
|
||||
{selectAppPlatformDisplay}
|
||||
{dependencyDisplay}
|
||||
{upgradeDisplay}
|
||||
{pinnedManagerDisplay}
|
||||
{configDisplay}
|
||||
{infoDisplay}
|
||||
{confirmDisplay}
|
||||
{downloadDisplay}
|
||||
{rebootDisplay}
|
||||
{appBusyDisplay}
|
||||
{errorDisplay}
|
||||
</div>
|
||||
);
|
||||
@@ -194,19 +292,27 @@ const mapStateToProps = state => {
|
||||
AllowDownload: state.download.AllowDownload,
|
||||
AllowMount: state.common.AllowMount,
|
||||
AppPlatform: state.common.AppPlatform,
|
||||
AppBusy: state.common.AppBusy,
|
||||
AppBusyTransparent: state.common.AppBusyTransparent,
|
||||
AppReady: state.common.AppReady,
|
||||
DismissDependencies: state.install.DismissDependencies,
|
||||
DisplayConfiguration: state.mounts.DisplayConfiguration,
|
||||
DisplayConfirmYesNo: state.common.DisplayConfirmYesNo,
|
||||
DisplayError: state.error.DisplayError,
|
||||
DisplayExport: state.skynet.DisplayExport,
|
||||
DisplayImport: state.skynet.DisplayImport,
|
||||
DisplayInfo: state.error.DisplayInfo,
|
||||
DisplayPinnedManager: state.pinned.DisplayPinnedManager,
|
||||
DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform,
|
||||
DismissNewReleasesAvailable: state.relver.DismissNewReleasesAvailable,
|
||||
DownloadActive: state.download.DownloadActive,
|
||||
InstallActive: state.install.InstallActive,
|
||||
InstalledVersion: state.relver.InstalledVersion,
|
||||
LocationsLookup: state.relver.LocationsLookup,
|
||||
MissingDependencies: state.install.MissingDependencies,
|
||||
MountsBusy: state.mounts.MountsBusy,
|
||||
NewReleasesAvailable: state.relver.NewReleasesAvailable,
|
||||
NewReleasesAvailable2: state.relver.NewReleasesAvailable2,
|
||||
Platform: state.common.Platform,
|
||||
ProviderState: state.mounts.ProviderState,
|
||||
RebootRequired: state.common.RebootRequired,
|
||||
@@ -222,9 +328,11 @@ const mapStateToProps = state => {
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
displaySelectAppPlatform: display => dispatch(displaySelectAppPlatform(display)),
|
||||
loadReleases: ()=> dispatch(loadReleases()),
|
||||
loadReleases: () => dispatch(loadReleases()),
|
||||
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
|
||||
saveState: () => dispatch(saveState()),
|
||||
setDismissNewReleasesAvailable: dismiss => dispatch(setDismissNewReleasesAvailable(dismiss)),
|
||||
setNewReleasesAvailable: items => dispatch(setNewReleasesAvailable(items)),
|
||||
setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)),
|
||||
};
|
||||
};
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
@@ -30,12 +30,19 @@
|
||||
"EventLevel": "Internally, events are fired during certain operations. This setting determines which events should be logged to repertory.log. Valid values are Error, Warn, Normal, Debug, and Verbose.",
|
||||
"EvictionDelaySeconds": "Number of seconds to wait after all file handles are closed before allowing file to be evicted from cache.",
|
||||
"EvictionDelayMinutes": "Number of minutes to wait after all file handles are closed before allowing file to be evicted from cache.",
|
||||
"EvictionUsesAccessedTime": "Use oldest accessed time instead of oldest modified time when evicting files.",
|
||||
"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.",
|
||||
"MaxUploadCount": "Maximum number of simultaneous uploads.",
|
||||
"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/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/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.",
|
||||
"RetryReadCount": "Number of times to retry a failed read operation. A 1 second delay is used between retries. This setting applies to downloads only.",
|
||||
"RingBufferFileSize": "The size of the ring buffer file in MiB. Default is 512. Valid values are: 64, 128, 256, 512, 1024."
|
||||
},
|
||||
"SkynetConfig": {
|
||||
"EncryptionToken": "Encryption token used to encrypt files in Skynet. Set this value once as it applies to all files stored.",
|
||||
"PortalList": "List of Skynet portals used to load-balance uploads and downloads."
|
||||
}
|
||||
}
|
||||
|
||||
21
src/components/ApplicationBusy/ApplicationBusy.js
Normal file
21
src/components/ApplicationBusy/ApplicationBusy.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import Box from '../UI/Box/Box';
|
||||
import Loader from 'react-loader-spinner';
|
||||
import Text from '../UI/Text/Text';
|
||||
|
||||
export default ({title}) => {
|
||||
return (
|
||||
<Box dxStyle={{padding: 'var(--default_spacing)'}}>
|
||||
<Text
|
||||
text={title || 'Please Wait...'}
|
||||
textAlign={'center'}
|
||||
type={'Heading1'}/>
|
||||
<div style={{paddingLeft: 'calc(50% - 16px)', paddingTop: 'var(--default_spacing)'}}>
|
||||
<Loader color={'var(--heading_text_color)'}
|
||||
height={32}
|
||||
width={32}
|
||||
type='TailSpin'/>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -22,9 +22,9 @@ export default connect(mapStateToProps)(props => {
|
||||
</td>
|
||||
<td>
|
||||
{props.AllowDownload ?
|
||||
<a href={void(0)}
|
||||
className={'DependencyLink'}
|
||||
onClick={()=>{props.onDownload(); return false;}}><u>Install</u></a> :
|
||||
<a href={'#'}
|
||||
className={'DependencyLink'}
|
||||
onClick={()=>{props.onDownload(); return false;}}><u>Install</u></a> :
|
||||
'Installing...'}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -32,4 +32,4 @@ export default connect(mapStateToProps)(props => {
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,15 +31,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
);
|
||||
});
|
||||
|
||||
let dismissDisplay;
|
||||
if (props.AllowDismissDependencies) {
|
||||
dismissDisplay = (
|
||||
<div style={{float: 'right', margin: 0, paddingRight: '4px', boxSizing: 'border-box', display: 'block'}}>
|
||||
<b style={{cursor: 'pointer'}}
|
||||
onClick={()=>props.setDismissDependencies(true)}>X
|
||||
</b>
|
||||
</div>);
|
||||
}
|
||||
const dismissDisplay = (
|
||||
<div style={{float: 'right', margin: 0, paddingRight: '4px', boxSizing: 'border-box', display: 'block'}}>
|
||||
<a href={'#'}
|
||||
onClick={props.AllowDismissDependencies ? () => props.setDismissDependencies(true) : e => e.preventDefault()}
|
||||
style={{cursor: props.AllowDismissDependencies ? 'pointer' : 'no-drop'}}>X</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box dxStyle={{width: '300px', height: 'auto', padding: '5px'}}>
|
||||
@@ -50,4 +48,4 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
{items}
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
.InfoDetailsContent {
|
||||
max-height: 60vh;
|
||||
min-width: 80vw;
|
||||
max-width: 90vw;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
white-space: pre-line;
|
||||
margin-bottom: var(--default_spacing);
|
||||
}
|
||||
}
|
||||
|
||||
.InfoDetailsContent.Alternate {
|
||||
white-space: pre;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,50 @@ const mapDispatchToProps = dispatch => {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
let msg = props.InfoMessage.message;
|
||||
const classes = ['InfoDetailsContent'];
|
||||
|
||||
let changed = true;
|
||||
let copyable = false;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
if (msg.startsWith('!alternate!')) {
|
||||
classes.push('Alternate');
|
||||
msg = msg.substr('!alternate!'.length);
|
||||
changed = true;
|
||||
}
|
||||
if (msg.startsWith('!copyable!')) {
|
||||
classes.push('Copyable');
|
||||
msg = msg.substr('!copyable!'.length);
|
||||
copyable = changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
const scrollToTop = textArea => {
|
||||
if (!textArea.firstClick) {
|
||||
textArea.firstClick = true;
|
||||
textArea.scrollTop = 0;
|
||||
textArea.setSelectionRange(0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<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>
|
||||
<div className={classes.join(' ')}>
|
||||
{
|
||||
copyable ? (
|
||||
<textarea autoFocus
|
||||
rows={9}
|
||||
value={msg}
|
||||
className={'SkynetImportTextArea'}
|
||||
onClick={e => scrollToTop(e.target)}/>
|
||||
) : (
|
||||
<p style={{textAlign: 'left'}}>{msg}</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<Button clicked={props.dismissInfo}>Dismiss</Button>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
57
src/components/NewReleases/NewRelease/NewRelease.js
Normal file
57
src/components/NewReleases/NewRelease/NewRelease.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import * as Constants from '../../../constants';
|
||||
import Button from '../../UI/Button/Button';
|
||||
import {formatLinesForDisplay, getChangesForRepertoryVersion} from '../../../utils';
|
||||
import {
|
||||
notifyError,
|
||||
notifyInfo
|
||||
} from '../../../redux/actions/error_actions';
|
||||
import {installReleaseByVersion} from '../../../redux/actions/install_actions';
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
installReleaseByVersion: (release, version) => dispatch(installReleaseByVersion(release, version)),
|
||||
notifyError: msg => dispatch(notifyError(msg)),
|
||||
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(null, mapDispatchToProps)(({dismiss, release, lastItem, notifyError, notifyInfo, installReleaseByVersion}) => {
|
||||
const title = '[' + Constants.RELEASE_TYPES[release.Release] + '] ' + release.Display;
|
||||
const installReleaseVersion = () => {
|
||||
dismiss();
|
||||
installReleaseByVersion(release.Release, release.Version);
|
||||
};
|
||||
const displayChanges = async () => {
|
||||
try {
|
||||
const lines = await getChangesForRepertoryVersion(release.VersionString);
|
||||
notifyInfo(title, formatLinesForDisplay(lines));
|
||||
} catch (e) {
|
||||
notifyError(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{title}</h2>
|
||||
<table cellSpacing={0} cellPadding={0} width="97%">
|
||||
<tbody>
|
||||
<tr style={{height: '4px'}}/>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<Button buttonStyles={{width: '100%'}} clicked={displayChanges}>Changes</Button>
|
||||
</td>
|
||||
<td>
|
||||
<div style={{width: 'var(--default_spacing)'}}/>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<Button buttonStyles={{width: '100%'}} clicked={installReleaseVersion}>Install</Button>
|
||||
</td>
|
||||
</tr>
|
||||
{lastItem ? null : <tr style={{height: 'var(--default_spacing)'}}/>}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
11
src/components/NewReleases/NewReleases.css
Normal file
11
src/components/NewReleases/NewReleases.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.NewReleasesHeading {
|
||||
text-align: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.NewReleasesContent {
|
||||
max-height: 60vh;
|
||||
min-width: 50vw;
|
||||
overflow-y: auto;
|
||||
margin-bottom: var(--default_spacing);
|
||||
}
|
||||
38
src/components/NewReleases/NewReleases.js
Normal file
38
src/components/NewReleases/NewReleases.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import Box from '../UI/Box/Box';
|
||||
import Button from '../UI/Button/Button';
|
||||
import NewRelease from './NewRelease/NewRelease';
|
||||
import './NewReleases.css';
|
||||
import {setDismissNewReleasesAvailable} from '../../redux/actions/release_version_actions';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
NewReleasesAvailable: state.relver.NewReleasesAvailable,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
dismissNewReleasesAvailable: () => dispatch(setDismissNewReleasesAvailable(true)),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
const newReleases = props.NewReleasesAvailable.map((i, idx) => {
|
||||
return <NewRelease dismiss={props.dismissNewReleasesAvailable}
|
||||
key={'new_release_' + i.Release + '_' + i.Version}
|
||||
lastItem={idx === (props.NewReleasesAvailable.length - 1)}
|
||||
release={i} />;
|
||||
});
|
||||
|
||||
return (
|
||||
<Box dxDark dxStyle={{padding: 'var(--default_spacing)'}}>
|
||||
<h1 className={'NewReleasesHeading'}>New Repertory Versions Available</h1>
|
||||
<div className={'NewReleasesContent'}>
|
||||
{newReleases}
|
||||
</div>
|
||||
<Button clicked={props.dismissNewReleasesAvailable}>Dismiss</Button>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import {downloadItem} from '../../redux/actions/download_actions';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
AllowMount: state.common.AllowMount,
|
||||
AppPlatform: state.common.AppPlatform,
|
||||
DismissDependencies: state.install.DismissDependencies,
|
||||
DownloadActive: state.download.DownloadActive,
|
||||
@@ -35,6 +36,12 @@ const mapDispatchToProps = dispatch => {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
const getSelectedVersion = () => {
|
||||
return (props.ReleaseVersion === -1) ?
|
||||
'unavailable' :
|
||||
props.VersionLookup[Constants.RELEASE_TYPES[props.Release]][props.ReleaseVersion];
|
||||
};
|
||||
|
||||
const handleDownloadRelease = () => {
|
||||
const fileName = props.version + '.zip';
|
||||
props.downloadItem(fileName, Constants.INSTALL_TYPES.Release, props.LocationsLookup[props.version].urls);
|
||||
@@ -52,7 +59,10 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
};
|
||||
|
||||
const text = props.InstalledVersion + ' [' + props.AppPlatform + ']';
|
||||
const disabled = props.DownloadActive || props.InstallActive || props.MountsBusy;
|
||||
const disabled = props.DownloadActive ||
|
||||
props.InstallActive ||
|
||||
props.MountsBusy ||
|
||||
(!props.AllowMount && (getSelectedVersion() !== 'unavailable')) ;
|
||||
const releaseExtracting = (props.InstallType === Constants.INSTALL_TYPES.Release);
|
||||
|
||||
let optionsDisplay = [];
|
||||
@@ -143,4 +153,4 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
{optionsDisplay}
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import './Button.css';
|
||||
export default props => {
|
||||
return (
|
||||
<button disabled={props.disabled}
|
||||
autoFocus={props.autoFocus}
|
||||
className={'Button'}
|
||||
style={props.buttonStyles}
|
||||
onClick={props.clicked}>{props.children}</button>
|
||||
|
||||
@@ -68,9 +68,9 @@ label.CheckBoxLabel .CheckBoxCheckMark:after {
|
||||
top: 1px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid var(--heading_text_color);
|
||||
border: solid var(--heading_other_text_color);
|
||||
border-width: 0 3px 3px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export default props => {
|
||||
<div className={'CheckBoxOwner'}>
|
||||
<label className='CheckBoxLabel'>{props.label}
|
||||
<input checked={JSON.parse(props.checked)}
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.disabled}
|
||||
onChange={props.changed}
|
||||
type='checkbox'/>
|
||||
@@ -13,4 +14,4 @@ export default props => {
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6,30 +6,18 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.DropDownSelect {
|
||||
.DropDownSelect, .DropDownSelect.Alt {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
border-radius: var(--border_radius);
|
||||
background: rgba(10, 10, 20, 0.75);
|
||||
border-color: rgba(10, 10, 20, 0.9);
|
||||
color: var(--text_color);
|
||||
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);
|
||||
background: var(--control_background);
|
||||
border: none;
|
||||
color: var(--text_color);
|
||||
box-sizing: border-box;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.DropDownSelect.Auto {
|
||||
@@ -54,4 +42,4 @@
|
||||
.DropDownSelect:active,
|
||||
.DropDownSelect.active {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export default props => {
|
||||
return (
|
||||
<div className={'DropDown'}>
|
||||
<select className={'DropDownSelect' + (props.auto ? ' Auto ' : '') + (props.alt ? ' Alt ' : '') }
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.disabled}
|
||||
onChange={props.changed}
|
||||
value={props.selected}>
|
||||
@@ -19,4 +20,4 @@ export default props => {
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import './Loading.css'
|
||||
import Loader from 'react-loader-spinner';
|
||||
|
||||
export default props => {
|
||||
export default () => {
|
||||
return (
|
||||
<div
|
||||
className={'Loading'}>
|
||||
@@ -13,4 +13,4 @@ export default props => {
|
||||
type='ThreeDots'/>
|
||||
</div>
|
||||
</div>);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.Modal.Transparent {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.ModalContent {
|
||||
position: fixed;
|
||||
width: auto;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import './Modal.css'
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
|
||||
export default props => {
|
||||
let modalStyles = [];
|
||||
@@ -11,12 +13,19 @@ export default props => {
|
||||
contentStyles.push('ModalCritical');
|
||||
}
|
||||
|
||||
if (props.transparent) {
|
||||
modalStyles.push('Transparent');
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={modalStyles.join(' ')}
|
||||
onClick={props.clicked}>
|
||||
<div className={contentStyles.join(' ')}>
|
||||
{props.children}
|
||||
<FocusTrap active={!props.disableFocusTrap}>
|
||||
<div
|
||||
className={modalStyles.join(' ')}
|
||||
onClick={props.clicked}>
|
||||
<div className={contentStyles.join(' ')}>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
</div>);
|
||||
};
|
||||
</FocusTrap>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,5 +17,9 @@
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.65;
|
||||
}
|
||||
color: var(--heading_text_color);
|
||||
}
|
||||
|
||||
.UpgradeIcon.Release {
|
||||
color: var(--heading_other_text_color);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import React from 'react';
|
||||
import './UpgradeIcon.css';
|
||||
import availableImage from '../../assets/images/release_available.png';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
|
||||
export default props => {
|
||||
const styles = ['UpgradeIcon'];
|
||||
let placement = 'left';
|
||||
let toolTipText = 'UI Upgrade Available';
|
||||
if (props.release) {
|
||||
placement='bottom';
|
||||
styles.push('Release');
|
||||
}
|
||||
|
||||
if (props.release || props.newReleases) {
|
||||
toolTipText = 'New Release Available';
|
||||
}
|
||||
|
||||
@@ -16,13 +22,14 @@ export default props => {
|
||||
(
|
||||
<div className={'UpgradeIconOwner'}>
|
||||
<p data-tip='' data-for={placement}>
|
||||
<img alt=''
|
||||
onClick={props.clicked}
|
||||
src={availableImage}
|
||||
className={'UpgradeIcon'}/>
|
||||
<a href={'#'}
|
||||
className={styles.join(' ')}
|
||||
onClick={props.clicked}>
|
||||
<FontAwesomeIcon icon={faExclamationTriangle}/>
|
||||
</a>
|
||||
</p>
|
||||
<ReactTooltip id={placement} place={placement}>{toolTipText}</ReactTooltip>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
};
|
||||
};
|
||||
|
||||
165
src/constants.js
165
src/constants.js
@@ -1,63 +1,110 @@
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.DEV_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n' +
|
||||
'MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEKfZmq5mMAtD4kSt2Gc/5J\n' +
|
||||
'H+HHTYtUZE6YYvsvz8TNG/bNL67ZtNRyaoMyhLTfIN4rPBNLUfD+owNS+u5Yk+lS\n' +
|
||||
'ZLYyOuhoCZIFefayYqKLr42G8EeuRbx0IMzXmJtN0a4rqxlWhkYufJubpdQ+V4DF\n' +
|
||||
'oeupcPdIATaadCKVeZC7A0G0uaSwoiAVMG5dZqjQW7F2LoQm3PhNkPvAybIJ6vBy\n' +
|
||||
'LqdBegS1JrDn43x/pvQHzLO+l+FIG23D1F7iF+yZm3DkzBdcmi/mOMYs/rXZpBym\n' +
|
||||
'2/kTuSGh5buuJCeyOwR8N3WdvXw6+KHMU/wWU8qTCTT87mYbzH4YR8HgkjkLHxAO\n' +
|
||||
'5waHK6vMu0TxugCdJmVV6BSbiarJsh66VRosn7+6hlq6AdgksxqCeNELZBS+LBki\n' +
|
||||
'tb5hKyL+jNZnaHiR0U7USWtmnqZG6FVVRzlCnxP7tZo5O5Ex9AAFGz5JzOzsFNbv\n' +
|
||||
'xwQ0zqaTQOze+MJbkda7JfRoC6TncD0+3hoXsiaF4mCn8PqUCn0DwhglcRucZlST\n' +
|
||||
'ZvDNDo1WAtxPJebb3aS6uymNhBIquQbVAWxVO4eTrOYEgutxwkHE3yO3is+ogp8d\n' +
|
||||
'xot7f/+vzlbsbIDyuZBDe0fFkbTIMTU48QuUUVZpRKmKZTHQloz4EHqminbfX1sh\n' +
|
||||
'M7wvDkpJEtqbc0VnG/BukUzP6e7Skvgc7eF1sI3+8jH8du2rivZeZAl7Q2f+L9JA\n' +
|
||||
'BY9pjaxttxsud7V5jeFi4tKuDHi21/XhSjlJK2c2C4AiUEK5/WhtGbQ5JjmcOjRq\n' +
|
||||
'yXFRqLlerzOcop2kbtU3Ar230wOx3Dj23Wg8++lV3LU4U9vMR/t0qnSbCSGJys7m\n' +
|
||||
'ax2JpFlTwj/0wYuTlVFoNQHZJ1cdfyRiRBY4Ou7XO0W5hcBBKiYsC+neEeMMHdCe\n' +
|
||||
'iTDIW/ojcVTdFovl+sq3n1u4SBknE90JC/3H+TPE1s2iB+fwORVg0KPosQSNDS0A\n' +
|
||||
'7iK6AZCDC3YooFo+OzHkYMt9uLkXiXMSLx70az+qlIwOzVHKxCo7W/QpeKCXUCRZ\n' +
|
||||
'MMdlYEUs1PC8x2qIRUEVHuJ0XMTKNyOHmzVLuLK93wUWbToh+rdDxnbhX+emuESn\n' +
|
||||
'XH6aKiUwX4olEVKSylRUQw8nVckZGVWXzLDlgpzDrLHC8J8qHzFt7eCqOdiqsxhZ\n' +
|
||||
'x1U5LtugxwSWncTZ7vlKl0DuC/AWB7SuDi7bGRMSVp2n+MnD1VLKlsCclHXjIciE\n' +
|
||||
'W29n3G3lJ/sOta2sxqLd0j1XBQddrFXl5b609sIY81ocHqu8P2hRu5CpqJ/sGZC5\n' +
|
||||
'mMH3segHBkRj0xJcfOxceRLj1a+ULIIR3xL/3f8s5Id25TDo/nqBoCvu5PeCpo6L\n' +
|
||||
'9wIDAQAB\n' +
|
||||
'-----END PUBLIC KEY-----';
|
||||
Object.defineProperty(exports, '__esModule', {value : true});
|
||||
exports.DEV_PUBLIC_KEY =
|
||||
'-----BEGIN PUBLIC KEY-----\n' +
|
||||
'MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEKfZmq5mMAtD4kSt2Gc/5J\n' +
|
||||
'H+HHTYtUZE6YYvsvz8TNG/bNL67ZtNRyaoMyhLTfIN4rPBNLUfD+owNS+u5Yk+lS\n' +
|
||||
'ZLYyOuhoCZIFefayYqKLr42G8EeuRbx0IMzXmJtN0a4rqxlWhkYufJubpdQ+V4DF\n' +
|
||||
'oeupcPdIATaadCKVeZC7A0G0uaSwoiAVMG5dZqjQW7F2LoQm3PhNkPvAybIJ6vBy\n' +
|
||||
'LqdBegS1JrDn43x/pvQHzLO+l+FIG23D1F7iF+yZm3DkzBdcmi/mOMYs/rXZpBym\n' +
|
||||
'2/kTuSGh5buuJCeyOwR8N3WdvXw6+KHMU/wWU8qTCTT87mYbzH4YR8HgkjkLHxAO\n' +
|
||||
'5waHK6vMu0TxugCdJmVV6BSbiarJsh66VRosn7+6hlq6AdgksxqCeNELZBS+LBki\n' +
|
||||
'tb5hKyL+jNZnaHiR0U7USWtmnqZG6FVVRzlCnxP7tZo5O5Ex9AAFGz5JzOzsFNbv\n' +
|
||||
'xwQ0zqaTQOze+MJbkda7JfRoC6TncD0+3hoXsiaF4mCn8PqUCn0DwhglcRucZlST\n' +
|
||||
'ZvDNDo1WAtxPJebb3aS6uymNhBIquQbVAWxVO4eTrOYEgutxwkHE3yO3is+ogp8d\n' +
|
||||
'xot7f/+vzlbsbIDyuZBDe0fFkbTIMTU48QuUUVZpRKmKZTHQloz4EHqminbfX1sh\n' +
|
||||
'M7wvDkpJEtqbc0VnG/BukUzP6e7Skvgc7eF1sI3+8jH8du2rivZeZAl7Q2f+L9JA\n' +
|
||||
'BY9pjaxttxsud7V5jeFi4tKuDHi21/XhSjlJK2c2C4AiUEK5/WhtGbQ5JjmcOjRq\n' +
|
||||
'yXFRqLlerzOcop2kbtU3Ar230wOx3Dj23Wg8++lV3LU4U9vMR/t0qnSbCSGJys7m\n' +
|
||||
'ax2JpFlTwj/0wYuTlVFoNQHZJ1cdfyRiRBY4Ou7XO0W5hcBBKiYsC+neEeMMHdCe\n' +
|
||||
'iTDIW/ojcVTdFovl+sq3n1u4SBknE90JC/3H+TPE1s2iB+fwORVg0KPosQSNDS0A\n' +
|
||||
'7iK6AZCDC3YooFo+OzHkYMt9uLkXiXMSLx70az+qlIwOzVHKxCo7W/QpeKCXUCRZ\n' +
|
||||
'MMdlYEUs1PC8x2qIRUEVHuJ0XMTKNyOHmzVLuLK93wUWbToh+rdDxnbhX+emuESn\n' +
|
||||
'XH6aKiUwX4olEVKSylRUQw8nVckZGVWXzLDlgpzDrLHC8J8qHzFt7eCqOdiqsxhZ\n' +
|
||||
'x1U5LtugxwSWncTZ7vlKl0DuC/AWB7SuDi7bGRMSVp2n+MnD1VLKlsCclHXjIciE\n' +
|
||||
'W29n3G3lJ/sOta2sxqLd0j1XBQddrFXl5b609sIY81ocHqu8P2hRu5CpqJ/sGZC5\n' +
|
||||
'mMH3segHBkRj0xJcfOxceRLj1a+ULIIR3xL/3f8s5Id25TDo/nqBoCvu5PeCpo6L\n' +
|
||||
'9wIDAQAB\n' +
|
||||
'-----END PUBLIC KEY-----';
|
||||
|
||||
const REPERTORY_BRANCH = 'master';
|
||||
const REPERTORY_UI_BRANCH = 'master';
|
||||
const _REPERTORY_BRANCH = 'master';
|
||||
const _REPERTORY_UI_BRANCH = 'master';
|
||||
|
||||
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.REPERTORY_BRANCH = _REPERTORY_BRANCH;
|
||||
exports.REPERTORY_UI_BRANCH = _REPERTORY_UI_BRANCH;
|
||||
|
||||
exports.LINUX_DETECT_SCRIPT_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/detect_linux.sh';
|
||||
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' +
|
||||
_REPERTORY_BRANCH + '/releases_1.3.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_linux2.sh';
|
||||
|
||||
exports.LINUX_SELECTABLE_PLATFORMS = [
|
||||
'ubuntu18.04',
|
||||
'ubuntu18.10',
|
||||
'ubuntu19.04',
|
||||
'ubuntu19.10'
|
||||
'centos7',
|
||||
];
|
||||
|
||||
exports.WINFSP_VERSION_NAMES = [
|
||||
'WinFsp 2019',
|
||||
'WinFsp 2019.0',
|
||||
'WinFsp 2019.1',
|
||||
'WinFsp 2019.2',
|
||||
'WinFsp 2019.3',
|
||||
'WinFsp 2019.3 B1',
|
||||
'WinFsp 2019.3 B2',
|
||||
'WinFsp 2019.3 B3',
|
||||
'WinFsp 2019.3 B4',
|
||||
'WinFsp 2019.3 B5',
|
||||
'WinFsp 2020',
|
||||
'WinFsp 2020.0',
|
||||
'WinFsp 2020.1',
|
||||
'WinFsp 2020.2',
|
||||
'WinFsp 2021 B1',
|
||||
'WinFsp 2021 B2',
|
||||
'WinFsp 2021 Beta1',
|
||||
'WinFsp 2021 Beta2',
|
||||
];
|
||||
|
||||
exports.DATA_LOCATIONS = {
|
||||
linux: '~/.local/repertory/ui',
|
||||
darwin: '~/Library/Application Support/repertory/ui',
|
||||
win32: '%LOCALAPPDATA%\\repertory\\ui'
|
||||
linux : '~/.local/repertory/ui',
|
||||
darwin : '~/Library/Application Support/repertory/ui',
|
||||
win32 : '%LOCALAPPDATA%\\repertory\\ui'
|
||||
};
|
||||
|
||||
exports.REPERTORY_LOCATIONS = {
|
||||
linux : '~/.local/repertory',
|
||||
darwin : '~/Library/Application Support/repertory',
|
||||
win32 : '%LOCALAPPDATA%\\repertory'
|
||||
};
|
||||
|
||||
exports.S3_PROVIDER_LIST = [
|
||||
'Filebase',
|
||||
];
|
||||
|
||||
exports.S3_REGION_PROVIDER_REGION = [
|
||||
'us-east-1',
|
||||
];
|
||||
|
||||
exports.S3_PROVIDER_URL = {
|
||||
'Filebase' : 'https://s3.filebase.com',
|
||||
};
|
||||
|
||||
exports.PROVIDER_LIST = [
|
||||
'Sia',
|
||||
'ScPrime'
|
||||
'Skynet',
|
||||
'ScPrime',
|
||||
];
|
||||
|
||||
exports.PROVIDER_ARG = {
|
||||
sia: '',
|
||||
scprime: '-sp'
|
||||
sia : '',
|
||||
skynet : '-sk',
|
||||
scprime : '-sp',
|
||||
s3 : '-s3',
|
||||
};
|
||||
|
||||
exports.DEFAULT_RELEASE = 0;
|
||||
exports.RELEASE_TYPES = [
|
||||
'Release',
|
||||
'RC',
|
||||
@@ -66,10 +113,10 @@ exports.RELEASE_TYPES = [
|
||||
];
|
||||
|
||||
exports.INSTALL_TYPES = {
|
||||
Dependency: 'dependency',
|
||||
Release: 'release',
|
||||
TestRelease: 'test_release',
|
||||
Upgrade: 'upgrade',
|
||||
Dependency : 'dependency',
|
||||
Release : 'release',
|
||||
TestRelease : 'test_release',
|
||||
Upgrade : 'upgrade',
|
||||
};
|
||||
|
||||
exports.IPC_Browse_Directory = 'browse_directory';
|
||||
@@ -96,6 +143,9 @@ exports.IPC_Download_File = 'download_file';
|
||||
exports.IPC_Download_File_Complete = 'download_file_complete';
|
||||
exports.IPC_Download_File_Progress = 'download_file_progress';
|
||||
|
||||
exports.IPC_Export_Skylinks = 'export_skylinks';
|
||||
exports.IPC_Export_Skylinks_Reply = 'export_skylinks_reply';
|
||||
|
||||
exports.IPC_Extract_Release = 'extract_release';
|
||||
exports.IPC_Extract_Release_Complete = 'extract_release_complete';
|
||||
|
||||
@@ -105,12 +155,27 @@ exports.IPC_Get_Config_Reply = 'get_config_reply';
|
||||
exports.IPC_Get_Config_Template = 'get_config_template';
|
||||
exports.IPC_Get_Config_Template_Reply = 'get_config_template_reply';
|
||||
|
||||
exports.IPC_Get_Directory_Items = 'get_directory_items';
|
||||
exports.IPC_Get_Directory_Items_Reply = 'get_directory_items_reply';
|
||||
|
||||
exports.IPC_Get_Pinned_Files = 'get_pinned_files';
|
||||
exports.IPC_Get_Pinned_Files_Reply = 'get_pinned_files_reply';
|
||||
|
||||
exports.IPC_Get_Pinned_Files_Status = 'get_pinned_files_status';
|
||||
exports.IPC_Get_Pinned_Files_Status_Reply = 'get_pinned_files_status_reply';
|
||||
|
||||
exports.IPC_Get_Platform = 'get_platform';
|
||||
exports.IPC_Get_Platform_Reply = 'get_platform_reply';
|
||||
|
||||
exports.IPC_Get_State = 'get_state';
|
||||
exports.IPC_Get_State_Reply = 'get_state_reply';
|
||||
|
||||
exports.IPC_Grab_Skynet_Tree = 'grab_skynet_tree';
|
||||
exports.IPC_Grab_Skynet_Tree_Reply = 'grab_skynet_tree_reply';
|
||||
|
||||
exports.IPC_Import_Skylinks = 'import_skylinks';
|
||||
exports.IPC_Import_Skylinks_Reply = 'import_skylinks_reply';
|
||||
|
||||
exports.IPC_Install_Dependency = 'install_dependency';
|
||||
exports.IPC_Install_Dependency_Reply = 'install_dependency_reply';
|
||||
|
||||
@@ -120,13 +185,15 @@ 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_Remove_Mount = 'remove_mount';
|
||||
exports.IPC_Remove_Mount_Reply = 'remove_mount_reply';
|
||||
|
||||
exports.IPC_Reboot_System = 'reboot_system';
|
||||
|
||||
exports.IPC_Save_State = 'save_state';
|
||||
|
||||
exports.IPC_Set_Pinned = 'set_pinned';
|
||||
|
||||
exports.IPC_Show_Window = 'show_window';
|
||||
|
||||
exports.IPC_Set_Config_Values = 'set_config_values';
|
||||
|
||||
13
src/containers/AddMount/AddMount.css
Normal file
13
src/containers/AddMount/AddMount.css
Normal file
@@ -0,0 +1,13 @@
|
||||
.AddRemoteMount {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.AddMountButtons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.AddMountButton {
|
||||
flex: 1;
|
||||
}
|
||||
246
src/containers/AddMount/AddMount.js
Normal file
246
src/containers/AddMount/AddMount.js
Normal file
@@ -0,0 +1,246 @@
|
||||
import React from 'react';
|
||||
import {Component} from 'react';
|
||||
import './AddMount.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, addS3Mount} from '../../redux/actions/mount_actions';
|
||||
import {createModalConditionally} from '../../utils';
|
||||
import DropDown from '../../components/UI/DropDown/DropDown';
|
||||
import * as Constants from '../../constants';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
RemoteMounts: state.mounts.RemoteMounts,
|
||||
S3Mounts: state.mounts.S3Mounts,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
addRemoteMount: (hostNameOrIp, port, token) => dispatch(addRemoteMount(hostNameOrIp, port, token)),
|
||||
addS3Mount: (name, accessKey, secretKey, region, bucketName, url) => dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName, url)),
|
||||
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
|
||||
}
|
||||
};
|
||||
|
||||
const default_state = {
|
||||
AccessKey: '',
|
||||
BucketName: '',
|
||||
DisplayRemote: false,
|
||||
DisplayS3: false,
|
||||
HostNameOrIp: '',
|
||||
Name: '',
|
||||
Port: 20000,
|
||||
Provider: Constants.S3_PROVIDER_LIST[0],
|
||||
Region: Constants.S3_REGION_PROVIDER_REGION[0],
|
||||
SecretKey: '',
|
||||
Token: '',
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class extends Component {
|
||||
state = {
|
||||
...default_state,
|
||||
};
|
||||
|
||||
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({
|
||||
DisplayRemote: false
|
||||
}, () => {
|
||||
this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token);
|
||||
this.setState({
|
||||
...default_state,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addS3Mount = () => {
|
||||
if (this.state.Name.length === 0) {
|
||||
this.props.notifyError('Name cannot be empty.');
|
||||
} else if (this.state.AccessKey.length === 0) {
|
||||
this.props.notifyError('AccessKey cannot be empty.');
|
||||
} else if (this.state.SecretKey.length === 0) {
|
||||
this.props.notifyError('SecretKey cannot be empty.')
|
||||
} else {
|
||||
const provider = 'S3' + this.state.Name;
|
||||
if (this.props.S3Mounts.includes(provider)) {
|
||||
this.props.notifyError('Remote host already exists');
|
||||
} else {
|
||||
this.setState({
|
||||
DisplayS3: false
|
||||
}, () => {
|
||||
this.props.addS3Mount(this.state.Name, this.state.AccessKey, this.state.SecretKey,
|
||||
this.state.Region, this.state.BucketName, Constants.S3_PROVIDER_URL[this.state.Provider]);
|
||||
this.setState({
|
||||
...default_state,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAddS3Mount = () => {
|
||||
this.setState({
|
||||
DisplayRemote: false,
|
||||
DisplayS3: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleAddRemoteMount = () => {
|
||||
this.setState({
|
||||
DisplayRemote: true,
|
||||
DisplayS3: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const displayAddRemote = createModalConditionally(this.state.DisplayRemote, (
|
||||
<Box dxDark
|
||||
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
|
||||
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add Remote
|
||||
Mount</h1>
|
||||
<Text text={'Hostname or IP'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<input onChange={e => this.setState({HostNameOrIp: e.target.value.trim()})}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'text'}
|
||||
value={this.state.HostNameOrIp}/>
|
||||
<div style={{paddingTop: 'var(--default_spacing)'}}/>
|
||||
<Text text={'Port'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<input max={65535}
|
||||
min={1025}
|
||||
onChange={e => this.setState({Port: e.target.value})}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'number'}
|
||||
value={this.state.Port}/>
|
||||
<div style={{paddingTop: 'var(--default_spacing)'}}/>
|
||||
<Text text={'Remote Token'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<input onChange={e => this.setState({Token: e.target.value})}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'text'}
|
||||
value={this.state.Token}/>
|
||||
<div style={{paddingTop: 'var(--default_spacing)'}}/>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<Button buttonStyles={{width: '100%'}}
|
||||
clicked={() => this.addRemoteMount()}>OK</Button>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<Button buttonStyles={{width: '100%'}}
|
||||
clicked={() => this.setState({DisplayRemote: false})}>Cancel</Button>
|
||||
</div>
|
||||
</Box>
|
||||
));
|
||||
|
||||
const displayAddS3 = createModalConditionally(this.state.DisplayS3, (
|
||||
<Box dxDark
|
||||
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
|
||||
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add S3
|
||||
Mount</h1>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<Text text={'Name'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<Text text={'Provider'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
</div>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<input onChange={e => this.setState({Name: e.target.value.trim()})}
|
||||
className={'ConfigurationItemInput'}
|
||||
style={{width: '100%'}}
|
||||
type={'text'}
|
||||
value={this.state.Name}/>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<DropDown changed={e => this.setState({Provider: e.target.value})}
|
||||
items={Constants.S3_PROVIDER_LIST}
|
||||
selected={this.state.Provider}/>
|
||||
</div>
|
||||
<div style={{paddingTop: 'var(--default_spacing)'}}/>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<Text text={'Bucket Name (optional)'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<Text text={'Region'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
</div>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<input onChange={e => this.setState({BucketName: e.target.value})}
|
||||
className={'ConfigurationItemInput'}
|
||||
style={{width: '100%'}}
|
||||
type={'text'}
|
||||
value={this.state.BucketName}/>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<input onChange={e => this.setState({Region: e.target.value})}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'text'}
|
||||
value={this.state.Region}/>
|
||||
</div>
|
||||
<div style={{paddingTop: 'var(--default_spacing)'}}/>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<Text text={'Access Key'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<Text text={'Secret Key'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
</div>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<input onChange={e => this.setState({AccessKey: e.target.value})}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'text'}
|
||||
value={this.state.AccessKey}/>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<input onChange={e => this.setState({SecretKey: e.target.value})}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'text'}
|
||||
value={this.state.SecretKey}/>
|
||||
</div>
|
||||
<div style={{paddingTop: 'calc(var(--default_spacing) * 2)'}}/>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<div style={{width: '200%'}}/>
|
||||
<Button buttonStyles={{width: '100%'}}
|
||||
clicked={() => this.addS3Mount()}>OK</Button>
|
||||
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
|
||||
<Button buttonStyles={{width: '100%'}}
|
||||
clicked={() => this.setState({DisplayS3: false})}>Cancel</Button>
|
||||
</div>
|
||||
</Box>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={'AddMount'}>
|
||||
{displayAddRemote}
|
||||
{displayAddS3}
|
||||
<div className={'AddMountButtons'}>
|
||||
{this.props.remoteSupported ?
|
||||
<Button className={'AddMountButton'}
|
||||
clicked={this.handleAddRemoteMount}>Add Remote Mount</Button> : null}
|
||||
{this.props.remoteSupported && this.props.s3Supported ?
|
||||
<div style={{paddingRight: 'var(--default_spacing)'}}/> : null}
|
||||
{this.props.s3Supported ?
|
||||
<Button className={'AddMountButton'}
|
||||
clicked={this.handleAddS3Mount}>Add S3 Mount</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
.AddRemoteMount {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -3,4 +3,8 @@
|
||||
height: calc(100vh - (var(--default_spacing) * 4));
|
||||
padding: var(--default_spacing);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ConfigurationLink {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Modal from '../../components/UI/Modal/Modal';
|
||||
import IPCContainer from '../IPCContainer/IPCContainer';
|
||||
import {displayConfiguration} from '../../redux/actions/mount_actions';
|
||||
import {notifyError} from '../../redux/actions/error_actions';
|
||||
import {displayPinnedManager} from '../../redux/actions/pinned_manager_actions';
|
||||
|
||||
const Constants = require('../../constants');
|
||||
|
||||
@@ -27,11 +28,21 @@ class Configuration extends IPCContainer {
|
||||
Template: {}
|
||||
};
|
||||
|
||||
checkItemChanged = (itemA, itemB) => {
|
||||
if (itemA.type === 'string_array') {
|
||||
if (itemA.value.length !== itemB.value.length) {
|
||||
return true;
|
||||
}
|
||||
return itemA.value.filter(i => !itemB.value.includes(i)).length !== 0;
|
||||
}
|
||||
return itemA.value !== itemB.value;
|
||||
};
|
||||
|
||||
checkSaveRequired = () => {
|
||||
const changedItems = [];
|
||||
let i = 0;
|
||||
for (const item of this.state.ItemList) {
|
||||
if (this.state.OriginalItemList[i++].value !== item.value) {
|
||||
if (this.checkItemChanged(this.state.OriginalItemList[i++], item)) {
|
||||
changedItems.push(item);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +52,7 @@ class Configuration extends IPCContainer {
|
||||
const changedObjectItems = [];
|
||||
let j = 0;
|
||||
for (const item of this.state.ObjectLookup[key]) {
|
||||
if (this.state.OriginalObjectLookup[key][j++].value !== item.value) {
|
||||
if (this.checkItemChanged(this.state.OriginalObjectLookup[key][j++], item)) {
|
||||
changedObjectItems.push(item);
|
||||
}
|
||||
}
|
||||
@@ -72,6 +83,7 @@ class Configuration extends IPCContainer {
|
||||
this.sendRequest(Constants.IPC_Get_Config_Template, {
|
||||
Provider: this.props.DisplayConfiguration,
|
||||
Remote: this.props.DisplayRemoteConfiguration,
|
||||
S3: this.props.DisplayS3Configuration,
|
||||
Version: this.props.version,
|
||||
});
|
||||
}
|
||||
@@ -84,26 +96,29 @@ class Configuration extends IPCContainer {
|
||||
createItemList = (config, template) => {
|
||||
const objectList = [];
|
||||
const itemList = Object
|
||||
.keys(config)
|
||||
.map(key => {
|
||||
return {
|
||||
advanced: template[key] ? template[key].advanced : false,
|
||||
hide_remote: template[key] ? template[key].hide_remote : false,
|
||||
label: key,
|
||||
remote: template[key] ? template[key].remote : false,
|
||||
value: (template[key] && (template[key].type === 'object')) ?
|
||||
.keys(config)
|
||||
.map(key => {
|
||||
return {
|
||||
advanced: template[key] ? template[key].advanced : false,
|
||||
hide_remote: template[key] ? template[key].hide_remote : false,
|
||||
label: key,
|
||||
remote: template[key] ? template[key].remote : false,
|
||||
type: template[key] ? template[key].type : null,
|
||||
value: (template[key] && (template[key].type === 'object')) ?
|
||||
config[key] :
|
||||
(template[key] && (template[key].type === 'string_array')) ?
|
||||
config[key] :
|
||||
config[key].toString(),
|
||||
};
|
||||
})
|
||||
.filter(i=> {
|
||||
let ret = template[i.label];
|
||||
if (ret && (template[i.label].type === 'object')) {
|
||||
objectList.push(i);
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
};
|
||||
})
|
||||
.filter(i => {
|
||||
let ret = template[i.label];
|
||||
if (ret && (template[i.label].type === 'object')) {
|
||||
objectList.push(i);
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
return {
|
||||
ObjectList: objectList,
|
||||
ItemList: itemList,
|
||||
@@ -114,7 +129,7 @@ class Configuration extends IPCContainer {
|
||||
const itemList = [
|
||||
...this.state.ItemList
|
||||
];
|
||||
itemList[idx].value = target.value.toString();
|
||||
itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString();
|
||||
this.setState({
|
||||
ItemList: itemList
|
||||
});
|
||||
@@ -128,7 +143,7 @@ class Configuration extends IPCContainer {
|
||||
...this.state.ObjectLookup,
|
||||
};
|
||||
|
||||
itemList[idx].value = target.value.toString();
|
||||
itemList[idx].value = target.type === 'textarea' ? target.string_array : target.value.toString();
|
||||
objectLookup[name] = itemList;
|
||||
this.setState({
|
||||
ObjectLookup: objectLookup,
|
||||
@@ -163,6 +178,8 @@ class Configuration extends IPCContainer {
|
||||
ObjectLookup: objectLookup,
|
||||
OriginalItemList: itemListCopy,
|
||||
OriginalObjectLookup: objectLookupCopy,
|
||||
}, () => {
|
||||
|
||||
});
|
||||
} else {
|
||||
this.props.notifyError(arg.data.Error);
|
||||
@@ -173,10 +190,11 @@ class Configuration extends IPCContainer {
|
||||
if (arg.data.Success) {
|
||||
this.setState({
|
||||
Template: arg.data.Template,
|
||||
}, ()=> {
|
||||
}, () => {
|
||||
this.sendRequest(Constants.IPC_Get_Config, {
|
||||
Provider: this.props.DisplayConfiguration,
|
||||
Remote: this.props.DisplayRemoteConfiguration,
|
||||
S3: this.props.DisplayS3Configuration,
|
||||
Version: this.props.version,
|
||||
});
|
||||
});
|
||||
@@ -196,12 +214,14 @@ class Configuration extends IPCContainer {
|
||||
saveAndClose = () => {
|
||||
this.setState({
|
||||
Saving: true,
|
||||
}, ()=> {
|
||||
}, () => {
|
||||
const changedItems = [];
|
||||
for (const item of this.state.ChangedItems) {
|
||||
changedItems.push({
|
||||
Name: item.label,
|
||||
Value: item.value,
|
||||
Value: item.type === 'string_array' ?
|
||||
item.value.join(';') :
|
||||
item.value,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -210,7 +230,9 @@ class Configuration extends IPCContainer {
|
||||
for (const item of this.state.ChangedObjectLookup[key]) {
|
||||
changedItems.push({
|
||||
Name: key + '.' + item.label,
|
||||
Value: item.value,
|
||||
Value: item.type === 'string_array' ?
|
||||
item.value.join(';') :
|
||||
item.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -220,6 +242,7 @@ class Configuration extends IPCContainer {
|
||||
Items: changedItems,
|
||||
Provider: this.props.DisplayConfiguration,
|
||||
Remote: this.props.DisplayRemoteConfiguration,
|
||||
S3: this.props.DisplayS3Configuration,
|
||||
Version: this.props.version,
|
||||
});
|
||||
});
|
||||
@@ -251,44 +274,39 @@ class Configuration extends IPCContainer {
|
||||
<Modal>
|
||||
<Box dxStyle={{width: '40vw', padding: 'var(--default_spacing)'}}>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1>
|
||||
<table width='100%'><tbody>
|
||||
<table width='100%'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align='center' width='50%'><Button clicked={this.saveAndClose} disabled={this.state.Saving}>Yes</Button></td>
|
||||
<td align='center' width='50%'><Button clicked={this.props.hideConfiguration} disabled={this.state.Saving}>No</Button></td>
|
||||
<td align='center' width='50%'><Button clicked={this.saveAndClose}
|
||||
disabled={this.state.Saving}>Yes</Button>
|
||||
</td>
|
||||
<td align='center' width='50%'><Button clicked={this.props.hideConfiguration}
|
||||
disabled={this.state.Saving}>No</Button></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</tbody>
|
||||
</table>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const configurationItems = this.state.ItemList
|
||||
.map((k, i) => {
|
||||
return (
|
||||
((!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'}
|
||||
items={this.state.Template[k.label].items}
|
||||
key={i}
|
||||
label={k.label}
|
||||
template={this.state.Template[k.label]}
|
||||
value={k.value}/> :
|
||||
null)
|
||||
});
|
||||
let autoFocus = true;
|
||||
|
||||
let objectItems = [];
|
||||
for (const key of Object.keys(this.state.ObjectLookup)) {
|
||||
objectItems.push((
|
||||
<div key={key}>
|
||||
<h1>{key}</h1>
|
||||
<h2>{key}</h2>
|
||||
<div>
|
||||
{
|
||||
this.state.ObjectLookup[key].map((k, i) => {
|
||||
const shouldFocus = autoFocus;
|
||||
autoFocus = false;
|
||||
return (
|
||||
(!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)}
|
||||
autoFocus={shouldFocus}
|
||||
changed={e => this.handleObjectItemChanged(e, key, i)}
|
||||
grouping={key}
|
||||
items={this.state.Template[key].template[k.label].items}
|
||||
key={i}
|
||||
@@ -304,21 +322,56 @@ class Configuration extends IPCContainer {
|
||||
));
|
||||
}
|
||||
|
||||
const configurationItems = this.state.ItemList
|
||||
.map((k, i) => {
|
||||
const shouldFocus = autoFocus;
|
||||
autoFocus = false;
|
||||
return (
|
||||
((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
|
||||
<ConfigurationItem advanced={k.advanced}
|
||||
autoFocus={shouldFocus}
|
||||
changed={e => this.handleItemChanged(e, i)}
|
||||
grouping={'Settings'}
|
||||
items={this.state.Template[k.label].items}
|
||||
key={i}
|
||||
label={k.label}
|
||||
template={this.state.Template[k.label]}
|
||||
value={k.value}/> :
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={'Configuration'}>
|
||||
{confirmSave}
|
||||
<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 style={{
|
||||
float: 'right',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
marginTop: '-4px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'block'
|
||||
}}>
|
||||
<a href={'#'}
|
||||
onClick={this.checkSaveRequired}
|
||||
style={{cursor: 'pointer'}}>X</a>
|
||||
</div>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>{(
|
||||
this.props.DisplayRemoteConfiguration ?
|
||||
this.props.DisplayConfiguration.substr(6) :
|
||||
this.props.DisplayConfiguration) + ' Configuration'}</h1>
|
||||
this.props.DisplayConfiguration) + ' Configuration '}
|
||||
</h1>
|
||||
<div style={{overflowY: 'auto', height: '90%'}}>
|
||||
{this.props.MState.Mounted && (configurationItems.length > 0) ? <Button
|
||||
buttonStyles={{width: 'auto', height: 'auto', marginLeft: 'auto', marginRight: '4px'}}
|
||||
clicked={() => {
|
||||
this.props.displayPinnedManager(true);
|
||||
return false;
|
||||
}}> Pinned File Manager... </Button> : null}
|
||||
<div style={{marginBottom: '4px'}}/>
|
||||
{objectItems}
|
||||
{(configurationItems.length > 0) ? <h1>Settings</h1> : null}
|
||||
{(configurationItems.length > 0) ? <h2>Settings</h2> : null}
|
||||
{configurationItems}
|
||||
</div>
|
||||
</Box>
|
||||
@@ -331,12 +384,15 @@ const mapStateToProps = state => {
|
||||
return {
|
||||
DisplayConfiguration: state.mounts.DisplayConfiguration,
|
||||
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
|
||||
DisplayS3Configuration: state.mounts.DisplayS3Configuration,
|
||||
MState: state.mounts.MountState[state.mounts.DisplayConfiguration],
|
||||
Platform: state.common.Platform,
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
displayPinnedManager: display => dispatch(displayPinnedManager(display)),
|
||||
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
|
||||
hideConfiguration: () => dispatch(displayConfiguration(null, false)),
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ input.ConfigurationItemInput {
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
border-radius: var(--border_radius);
|
||||
background: rgba(160, 160, 160, 0.1);
|
||||
background: var(--control_background);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
@@ -16,6 +16,23 @@ input.ConfigurationItemInput {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea.ConfigurationItemInput {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
border-radius: var(--border_radius);
|
||||
background: var(--control_background);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
color: var(--text_color);
|
||||
box-sizing: border-box;
|
||||
resize: none;
|
||||
overflow-y: scroll;
|
||||
overflow:-moz-scrollbars-horizontal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ConfigurationInfo {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,18 @@ import CheckBox from '../../../components/UI/CheckBox/CheckBox';
|
||||
import {connect} from 'react-redux';
|
||||
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {notifyInfo} from '../../../redux/actions/error_actions';
|
||||
import {
|
||||
notifyError,
|
||||
notifyInfo
|
||||
} from '../../../redux/actions/error_actions';
|
||||
import settings from '../../../assets/settings';
|
||||
import DropDown from '../../../components/UI/DropDown/DropDown';
|
||||
import Password from '../../../containers/UI/Password/Password';
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg))
|
||||
notifyError: msg => dispatch(notifyError(msg)),
|
||||
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +23,9 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
const handleChanged = (e) => {
|
||||
const target = e.target;
|
||||
if (target.type === 'checkbox') {
|
||||
target.value = e.target.checked ? "true" : "false";
|
||||
target.value = e.target.checked ? 'true' : 'false';
|
||||
} else if (target.type === 'textarea') {
|
||||
e.target.string_array = String(e.target.value).replace(/\r\n/g,'\n').split('\n');
|
||||
}
|
||||
props.changed(target);
|
||||
};
|
||||
@@ -30,49 +37,71 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
props.notifyInfo(props.label, description);
|
||||
};
|
||||
|
||||
infoDisplay = <a href={void(0)}
|
||||
className={'ConfigurationInfo'}
|
||||
onClick={()=>{displayInfo(); return false;}}><FontAwesomeIcon icon={faInfoCircle}/></a>;
|
||||
infoDisplay = <a href={'#'}
|
||||
className={'ConfigurationInfo'}
|
||||
onClick={()=>{displayInfo(); return false;}}><FontAwesomeIcon icon={faInfoCircle}/></a>;
|
||||
}
|
||||
|
||||
|
||||
let data;
|
||||
switch (props.template.type) {
|
||||
case "bool":
|
||||
case 'bool':
|
||||
data = <CheckBox changed={handleChanged}
|
||||
checked={props.value}
|
||||
disabled={props.readOnly}/>;
|
||||
disabled={props.readOnly}
|
||||
autoFocus={props.autoFocus}/>;
|
||||
break;
|
||||
|
||||
case "double":
|
||||
case 'double':
|
||||
data = <input min={0.0}
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.readOnly}
|
||||
onChange={e=>handleChanged(e)}
|
||||
step={"0.01"}
|
||||
step={'0.01'}
|
||||
className={'ConfigurationItemInput'}
|
||||
type={'number'}
|
||||
value={parseFloat(props.value).toFixed(2)}/>;
|
||||
break;
|
||||
|
||||
case "list":
|
||||
case 'list':
|
||||
data = <DropDown alt
|
||||
auto
|
||||
autoFocus={props.autoFocus}
|
||||
changed={handleChanged}
|
||||
disabled={props.readOnly}
|
||||
items={props.items}
|
||||
selected={props.value} />;
|
||||
break;
|
||||
|
||||
case "string":
|
||||
data = <input onChange={e=>handleChanged(e)}
|
||||
className={'ConfigurationItemInput'}
|
||||
case 'string':
|
||||
if (props.template.subtype === 'password') {
|
||||
data = (
|
||||
<Password autoFocus={props.autoFocus}
|
||||
changed={s => handleChanged({
|
||||
target: {
|
||||
type: 'password',
|
||||
value: s,
|
||||
},
|
||||
})}
|
||||
disabled={props.readOnly}
|
||||
type={'text'}
|
||||
value={props.value}/>;
|
||||
mismatchHandler={() => props.notifyError('Passwords do not match')}
|
||||
value={props.value} />
|
||||
);
|
||||
} else {
|
||||
data = (
|
||||
<input onChange={e => handleChanged(e)}
|
||||
autoFocus={props.autoFocus}
|
||||
className={'ConfigurationItemInput'}
|
||||
disabled={props.readOnly}
|
||||
type={'text'}
|
||||
value={props.value}/>
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "uint8":
|
||||
case 'uint8':
|
||||
data = <input max={255}
|
||||
min={0}
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.readOnly}
|
||||
onChange={e=>handleChanged(e)}
|
||||
className={'ConfigurationItemInput'}
|
||||
@@ -80,9 +109,10 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint16":
|
||||
case 'uint16':
|
||||
data = <input max={65535}
|
||||
min={0}
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.readOnly}
|
||||
onChange={e=>handleChanged(e)}
|
||||
className={'ConfigurationItemInput'}
|
||||
@@ -90,9 +120,10 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint32":
|
||||
case 'uint32':
|
||||
data = <input max={4294967295}
|
||||
min={0}
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.readOnly}
|
||||
onChange={e=>handleChanged(e)}
|
||||
className={'ConfigurationItemInput'}
|
||||
@@ -100,9 +131,10 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint64":
|
||||
case 'uint64':
|
||||
data = <input max={18446744073709551615}
|
||||
min={0}
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.readOnly}
|
||||
onChange={e=>handleChanged(e)}
|
||||
className={'ConfigurationItemInput'}
|
||||
@@ -110,6 +142,33 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case 'string_array':
|
||||
data = (
|
||||
<textarea autoFocus={props.autoFocus}
|
||||
disabled={props.readOnly}
|
||||
rows={4}
|
||||
cols={36}
|
||||
onChange={e=>handleChanged(e)}
|
||||
className={'ConfigurationItemInput'}
|
||||
value={props.value.join('\n')} />
|
||||
);
|
||||
break;
|
||||
|
||||
case 'password':
|
||||
data = (
|
||||
<Password autoFocus={props.autoFocus}
|
||||
changed={s => handleChanged({
|
||||
target: {
|
||||
type: 'password',
|
||||
value: s,
|
||||
},
|
||||
})}
|
||||
disabled={props.readOnly}
|
||||
mismatchHandler={() => props.notifyError('Passwords do not match')}
|
||||
value={props.value} />
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
data = <div>{props.value}</div>;
|
||||
}
|
||||
@@ -121,12 +180,12 @@ export default connect(null, mapDispatchToProps)(props => {
|
||||
<tbody>
|
||||
<tr>
|
||||
{infoDisplay ?
|
||||
<td width='100%'>{infoDisplay} {props.label}</td> :
|
||||
<td width='100%'>{props.label}</td>}
|
||||
<td width='100%' valign={'top'}>{infoDisplay} {props.label}</td> :
|
||||
<td width='100%' valign={'top'}>{props.label}</td>}
|
||||
<td>{data}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,15 @@ 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 {
|
||||
displayConfiguration,
|
||||
removeMount,
|
||||
setProviderState
|
||||
} from '../../../redux/actions/mount_actions';
|
||||
import {
|
||||
displaySkynetExport,
|
||||
displaySkynetImport,
|
||||
} from '../../../redux/actions/skynet_actions'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import { faTrashAlt} from '@fortawesome/free-solid-svg-icons';
|
||||
import CheckBox from '../../../components/UI/CheckBox/CheckBox';
|
||||
@@ -23,8 +31,10 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
displayConfiguration: (provider, remote) => dispatch(displayConfiguration(provider, remote)),
|
||||
removeRemoteMount: provider => dispatch(removeRemoteMount(provider)),
|
||||
displayConfiguration: (provider, remote, s3) => dispatch(displayConfiguration(provider, remote, s3)),
|
||||
displaySkynetExport: display => dispatch(displaySkynetExport(display)),
|
||||
displaySkynetImport: display => dispatch(displaySkynetImport(display)),
|
||||
removeMount: provider => dispatch(removeMount(provider)),
|
||||
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
|
||||
}
|
||||
};
|
||||
@@ -53,7 +63,9 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
rowSpan={6}>
|
||||
<img alt=''
|
||||
height={'16px'}
|
||||
onClick={props.MState.AllowMount ? ()=>props.displayConfiguration(props.provider, props.remote) : e=>{e.preventDefault();}}
|
||||
onClick={props.MState.AllowMount ? () => props.displayConfiguration(props.provider, props.remote, props.s3) : e => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
src={configureImage}
|
||||
style={{padding: 0, border: 0, margin: 0, ...pointer}}
|
||||
width={'16px'}/>
|
||||
@@ -110,12 +122,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
width={19}/>;
|
||||
|
||||
const actionsDisplay = (
|
||||
<Button clicked={()=>props.clicked(props.provider, props.remote, !props.MState.Mounted, props.PState.MountLocation)}
|
||||
col={inputColumnSpan + 2}
|
||||
colSpan={21}
|
||||
disabled={!props.MState.AllowMount}
|
||||
row={secondRow}
|
||||
rowSpan={7}>
|
||||
<Button
|
||||
clicked={() => props.clicked(props.provider, props.remote, props.s3, !props.MState.Mounted, props.PState.MountLocation)}
|
||||
col={inputColumnSpan + 2}
|
||||
colSpan={21}
|
||||
disabled={!props.MState.AllowMount}
|
||||
row={secondRow}
|
||||
rowSpan={7}>
|
||||
{buttonDisplay}
|
||||
</Button>);
|
||||
|
||||
@@ -125,7 +138,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
row={secondRow}
|
||||
rowSpan={7}>
|
||||
<CheckBox changed={handleAutoMountChanged}
|
||||
checked={props.PState.AutoMount} label={'Auto-mount'}/>
|
||||
checked={props.PState.AutoMount}
|
||||
label={'Auto-mount'}/>
|
||||
</RootElem>
|
||||
);
|
||||
|
||||
@@ -141,26 +155,28 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
);
|
||||
|
||||
let removeControl;
|
||||
if (props.remote) {
|
||||
if (props.allowRemove) {
|
||||
const removeDisabled = !props.MState.AllowMount || props.MState.Mounted;
|
||||
const removeStyle = {
|
||||
cursor: removeDisabled ? 'no-drop' : 'pointer'
|
||||
};
|
||||
const handleRemoveMount = () => {
|
||||
if (!removeDisabled) {
|
||||
props.removeRemoteMount(props.provider);
|
||||
props.removeMount(props.provider);
|
||||
}
|
||||
};
|
||||
removeControl = (
|
||||
<a col={dimensions=>dimensions.columns - 6}
|
||||
href={void(0)}
|
||||
onClick={handleRemoveMount}
|
||||
row={secondRow + 3}
|
||||
style={removeStyle}>
|
||||
<FontAwesomeIcon icon={faTrashAlt}/>
|
||||
</a>);
|
||||
<RootElem col={dimensions=>dimensions.columns - 6}
|
||||
row={secondRow + 3}>
|
||||
<a href={'#'}
|
||||
onClick={handleRemoveMount}
|
||||
style={removeStyle}>
|
||||
<FontAwesomeIcon icon={faTrashAlt}/>
|
||||
</a>
|
||||
</RootElem>);
|
||||
}
|
||||
|
||||
const isSkynet = (props.provider === 'Skynet');
|
||||
return (
|
||||
<div className={'MountItem'}>
|
||||
<Grid noScroll>
|
||||
@@ -168,8 +184,32 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
|
||||
<Text
|
||||
col={configButton ? 6 : 0}
|
||||
rowSpan={5}
|
||||
text={props.remote ? props.provider.substr(6) : props.provider}
|
||||
type={'Heading1'}/>
|
||||
colSpan={90}
|
||||
text={props.remote ? props.provider.substr(6) : props.s3 ? props.provider.substr(2) : isSkynet ? props.provider + ' [EXPERIMENTAL]' : props.provider}
|
||||
textAlign={'Left'}
|
||||
type={'Heading2'}/>
|
||||
{(isSkynet && (props.MState.Mounted)) ? (
|
||||
<a href={'#'}
|
||||
col={(configButton ? 24 : 18) + 34}
|
||||
onClick={props.MState.AllowMount ? () => props.displaySkynetExport(true) : e => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
rowSpan={5}
|
||||
style={{...pointer, fontWeight: 'normal'}}>
|
||||
<u>Export</u>
|
||||
</a>
|
||||
) : null}
|
||||
{(isSkynet && (props.MState.Mounted)) ? (
|
||||
<a href={'#'}
|
||||
col={(configButton ? 24 + 13 : 18 + 13) + 34}
|
||||
onClick={props.MState.AllowMount ? () => props.displaySkynetImport(true) : e => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
rowSpan={5}
|
||||
style={{...pointer, fontWeight: 'normal'}}>
|
||||
<u>Import</u>
|
||||
</a>
|
||||
) : null}
|
||||
{inputControls}
|
||||
{actionsDisplay}
|
||||
{autoMountControl}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import AddRemoteMount from '../AddRemoteMount/AddRemoteMount';
|
||||
import AddMount from '../AddMount/AddMount';
|
||||
import Box from '../../components/UI/Box/Box';
|
||||
import Button from '../../components/UI/Button/Button';
|
||||
import {connect} from 'react-redux';
|
||||
@@ -80,6 +80,7 @@ class MountItems extends IPCContainer {
|
||||
|
||||
this.sendRequest(Constants.IPC_Detect_Mount, {
|
||||
RemoteMounts: this.props.RemoteMounts,
|
||||
S3Mounts: this.props.S3Mounts,
|
||||
Provider: provider,
|
||||
Version: this.props.InstalledVersion,
|
||||
});
|
||||
@@ -94,7 +95,7 @@ class MountItems extends IPCContainer {
|
||||
}
|
||||
};
|
||||
|
||||
displayRetryMount = (provider, remote, mountLocation, msg) => {
|
||||
displayRetryMount = (provider, remote, s3, mountLocation, msg) => {
|
||||
if (!this.state.RetryItems[provider]) {
|
||||
let retryItems = {
|
||||
...this.state.RetryItems
|
||||
@@ -121,7 +122,7 @@ class MountItems extends IPCContainer {
|
||||
const retrySeconds = retryItems[provider].RetrySeconds - 1;
|
||||
if (retrySeconds === 0) {
|
||||
this.cancelRetryMount(provider, () => {
|
||||
this.handleMountUnMount(provider, remote,true, mountLocation);
|
||||
this.handleMountUnMount(provider, remote, s3, true, mountLocation);
|
||||
});
|
||||
} else {
|
||||
retryItems[provider].RetrySeconds = retrySeconds;
|
||||
@@ -152,17 +153,18 @@ class MountItems extends IPCContainer {
|
||||
this.props.setProviderState(provider, state);
|
||||
};
|
||||
|
||||
handleMountUnMount = (provider, remote, mount, location) => {
|
||||
handleMountUnMount = (provider, remote, s3, mount, location) => {
|
||||
if (!location || (location.trim().length === 0)) {
|
||||
this.props.notifyError('Mount location is not set');
|
||||
} else {
|
||||
let allowAction = true;
|
||||
if (mount) {
|
||||
let result = remote ?
|
||||
let result = remote || s3 || provider === 'Skynet' ?
|
||||
{Valid: true, Success: true} :
|
||||
this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
|
||||
Provider: provider,
|
||||
Remote: remote,
|
||||
S3: s3,
|
||||
Version: this.props.InstalledVersion
|
||||
}).data;
|
||||
if (result.Success) {
|
||||
@@ -179,11 +181,11 @@ class MountItems extends IPCContainer {
|
||||
} else {
|
||||
allowAction = false;
|
||||
if ((result.Code === new Uint32Array([-1])[0]) || (result.Code === new Uint8Array([-1])[0])) {
|
||||
this.displayRetryMount(provider, remote, location, 'Failed to connect to ' + provider + ' daemon');
|
||||
this.displayRetryMount(provider, remote, s3, location, 'Failed to connect to ' + provider + ' daemon');
|
||||
} else if ((result.Code === new Uint32Array([-3])[0]) || (result.Code === new Uint8Array([-3])[0])) {
|
||||
this.displayRetryMount(provider, remote, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.');
|
||||
this.displayRetryMount(provider, remote, s3, location, 'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.');
|
||||
} else {
|
||||
this.displayRetryMount(provider, remote, location, 'Version check failed: ' + result.Error);
|
||||
this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -191,7 +193,7 @@ 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, remote, location, 'Version check failed: ' + result.Error);
|
||||
this.displayRetryMount(provider, remote, s3, location, 'Version check failed: ' + result.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,6 +207,7 @@ class MountItems extends IPCContainer {
|
||||
Location: location,
|
||||
Provider: provider,
|
||||
Remote: remote,
|
||||
S3: s3,
|
||||
Version: this.props.InstalledVersion,
|
||||
});
|
||||
} else {
|
||||
@@ -212,6 +215,7 @@ class MountItems extends IPCContainer {
|
||||
Location: location,
|
||||
Provider: provider,
|
||||
Remote: remote,
|
||||
S3: s3,
|
||||
Version: this.props.InstalledVersion,
|
||||
});
|
||||
}
|
||||
@@ -219,10 +223,27 @@ class MountItems extends IPCContainer {
|
||||
}
|
||||
};
|
||||
|
||||
getProviderList = () => {
|
||||
getProviderList = providersOnly => {
|
||||
const providerList = Constants.PROVIDER_LIST.filter(i => {
|
||||
return ((i === 'Skynet') && this.props.skynetSupported) ||
|
||||
((i === 'ScPrime') && this.props.scPrimeSupported) ||
|
||||
((i === 'Sia') && this.props.siaSupported);
|
||||
});
|
||||
|
||||
let remoteList = [];
|
||||
if (this.props.remoteSupported && !providersOnly) {
|
||||
remoteList = [...this.props.RemoteMounts];
|
||||
}
|
||||
|
||||
let s3List = [];
|
||||
if (this.props.s3Supported && !providersOnly) {
|
||||
s3List = [...this.props.S3Mounts];
|
||||
}
|
||||
|
||||
return [
|
||||
...Constants.PROVIDER_LIST,
|
||||
...this.props.RemoteMounts,
|
||||
...providerList,
|
||||
...remoteList,
|
||||
...s3List,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -298,7 +319,7 @@ class MountItems extends IPCContainer {
|
||||
this.props.ProviderState[provider].AutoMount &&
|
||||
!mounted &&
|
||||
(location.length > 0)) {
|
||||
this.handleMountUnMount(provider, this.props.RemoteMounts.includes(provider),true, location);
|
||||
this.handleMountUnMount(provider, this.props.RemoteMounts.includes(provider), this.props.S3Mounts.includes(provider), true, location);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -325,7 +346,11 @@ class MountItems extends IPCContainer {
|
||||
retryDisplay = (
|
||||
<Modal>
|
||||
<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>
|
||||
<h1 style={{
|
||||
textAlign: 'center',
|
||||
paddingBottom: 'var(--default_spacing)',
|
||||
color: 'var(--text_color_error)'
|
||||
}}>Mount Failed</h1>
|
||||
{retryList}
|
||||
</Box>
|
||||
</Modal>
|
||||
@@ -333,15 +358,17 @@ class MountItems extends IPCContainer {
|
||||
}
|
||||
|
||||
let footerItems = [];
|
||||
if (this.props.remoteSupported) {
|
||||
footerItems.push(<AddRemoteMount key={'hi_' + footerItems.length}/>);
|
||||
if (this.props.remoteSupported || this.props.s3Supported) {
|
||||
footerItems.push(<AddMount remoteSupported={this.props.remoteSupported}
|
||||
s3Supported={this.props.s3Supported}
|
||||
key={'hi_' + footerItems.length}/>);
|
||||
} else {
|
||||
footerItems.push(<div key={'hi_' + footerItems.length}
|
||||
style={{height: '27px'}}/>);
|
||||
}
|
||||
|
||||
let items = [];
|
||||
for (const provider of Constants.PROVIDER_LIST) {
|
||||
for (const provider of this.getProviderList(true)) {
|
||||
items.push((
|
||||
<MountItem allowRemove={false}
|
||||
browseClicked={this.handleBrowseLocation}
|
||||
@@ -368,15 +395,31 @@ class MountItems extends IPCContainer {
|
||||
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)
|
||||
}
|
||||
|
||||
if (this.props.s3Supported) {
|
||||
for (const provider of this.props.S3Mounts) {
|
||||
items.push((
|
||||
<MountItem allowRemove={true}
|
||||
browseClicked={this.handleBrowseLocation}
|
||||
changed={e => this.handleMountLocationChanged(provider, e.target.value)}
|
||||
clicked={this.handleMountUnMount}
|
||||
key={'it_' + items.length}
|
||||
provider={provider}
|
||||
s3/>
|
||||
));
|
||||
items.push(<div key={'it_' + items.length}
|
||||
style={{paddingTop: 'var(--default_spacing)'}}/>)
|
||||
}
|
||||
}
|
||||
|
||||
items.splice(items.length - 1, 1);
|
||||
|
||||
return (
|
||||
<div style={{margin: 0, padding: 0}}>
|
||||
{retryDisplay}
|
||||
<div className={this.props.remoteSupported ? 'MountItemsRemote' : 'MountItems'}>
|
||||
<div
|
||||
className={this.props.remoteSupported || this.props.s3Supported ? 'MountItemsRemote' : 'MountItems'}>
|
||||
{items}
|
||||
</div>
|
||||
<div style={{paddingTop: 'var(--default_spacing)'}}/>
|
||||
@@ -394,6 +437,7 @@ const mapStateToProps = state => {
|
||||
Platform: state.common.Platform,
|
||||
ProviderState: state.mounts.ProviderState,
|
||||
RemoteMounts: state.mounts.RemoteMounts,
|
||||
S3Mounts: state.mounts.S3Mounts,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
44
src/containers/PinnedManager/PinnedManager.css
Normal file
44
src/containers/PinnedManager/PinnedManager.css
Normal file
@@ -0,0 +1,44 @@
|
||||
.PinnedManager {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.PinnedManagerActiveDirectory {
|
||||
margin-top: var(--default_spacing);
|
||||
margin-bottom: var(--default_spacing);
|
||||
padding: 2px;
|
||||
width: calc(100% - 4px);
|
||||
overflow: auto;
|
||||
text-align: left;
|
||||
border-radius: var(--border_radius);
|
||||
background: var(--control_background);
|
||||
}
|
||||
|
||||
.PinnedManagerClose {
|
||||
float: right;
|
||||
flex: 0;
|
||||
padding: 0;
|
||||
margin-top: -4px;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.PinnedManagerHeading {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.PinnedManagerItems {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.PinnedManagerItemsOwner {
|
||||
margin: 0;
|
||||
padding: var(--default_spacing);
|
||||
border-radius: var(--border_radius);
|
||||
background: var(--control_background);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
180
src/containers/PinnedManager/PinnedManager.js
Normal file
180
src/containers/PinnedManager/PinnedManager.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import React from 'react';
|
||||
import './PinnedManager.css';
|
||||
import {connect} from 'react-redux';
|
||||
import IPCContainer from '../IPCContainer/IPCContainer';
|
||||
import {notifyApplicationBusy} from '../../redux/actions/common_actions';
|
||||
import {notifyError, notifyInfo} from '../../redux/actions/error_actions';
|
||||
import Box from '../../components/UI/Box/Box';
|
||||
import {displayPinnedManager} from '../../redux/actions/pinned_manager_actions';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faFolder} from '@fortawesome/free-solid-svg-icons';
|
||||
import Button from '../../components/UI/Button/Button';
|
||||
import CheckBox from '../../components/UI/CheckBox/CheckBox';
|
||||
|
||||
const Constants = require('../../constants');
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
DisplayConfiguration: state.mounts.DisplayConfiguration,
|
||||
DisplayRemoteConfiguration: state.mounts.DisplayRemoteConfiguration,
|
||||
DisplayS3Configuration: state.mounts.DisplayS3Configuration,
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
displayPinnedManager: display => dispatch(displayPinnedManager(display)),
|
||||
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
|
||||
notifyError: (msg, cb) => dispatch(notifyError(msg, false, cb)),
|
||||
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer {
|
||||
state = {
|
||||
active_directory: '/',
|
||||
items: [],
|
||||
previous: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setRequestHandler(Constants.IPC_Get_Directory_Items_Reply, this.onGetDirectoryItemsReply);
|
||||
this.grabDirectoryItems();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
|
||||
grabDirectoryItems = () => {
|
||||
this.sendRequest(Constants.IPC_Get_Directory_Items, {
|
||||
Provider: this.props.DisplayConfiguration,
|
||||
Remote: this.props.DisplayRemoteConfiguration,
|
||||
S3: this.props.DisplayS3Configuration,
|
||||
Version: this.props.version,
|
||||
Path: this.state.active_directory,
|
||||
});
|
||||
}
|
||||
|
||||
onGetDirectoryItemsReply = (_, {data}) => {
|
||||
if (data.Success) {
|
||||
const items = data.Items
|
||||
.filter(i => i.path !== '.' && (this.state.active_directory !== '/' || (i.path.substr(0, 1) !== '.')))
|
||||
.map(i => {
|
||||
return {
|
||||
...i,
|
||||
name: i.path === '..' ? i.path : i.path.substr(i.path.lastIndexOf('/') + 1),
|
||||
meta: {
|
||||
...i.meta,
|
||||
pinned: i.meta.pinned === '1',
|
||||
}
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
items,
|
||||
});
|
||||
} else {
|
||||
this.props.notifyError(data.Error, () => {
|
||||
this.props.displayPinnedManager(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createDirectory = (name, path, idx, total, item_idx) => {
|
||||
const style = {}
|
||||
if (item_idx + 1 !== total) {
|
||||
style.marginBottom = '4px';
|
||||
}
|
||||
return (
|
||||
<div key={'dir_' + idx} style={{...style}}>
|
||||
<Button buttonStyles={{textAlign: 'left'}}
|
||||
clicked={() => {
|
||||
const previous = [...this.state.previous];
|
||||
if (path === '..') {
|
||||
path = previous.pop();
|
||||
} else {
|
||||
previous.push(this.state.active_directory);
|
||||
}
|
||||
this.setState({
|
||||
items: [],
|
||||
active_directory: path,
|
||||
previous,
|
||||
}, () => {
|
||||
this.grabDirectoryItems();
|
||||
});
|
||||
}}>
|
||||
<FontAwesomeIcon icon={faFolder}
|
||||
fixedWidth
|
||||
color={'var(--heading_text_color)'}
|
||||
style={{padding: 0, margin: 0}}/>
|
||||
{name}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
createFile = (name, path, pinned, idx, total, item_idx) => {
|
||||
const style = {textAlign: 'left'}
|
||||
if (item_idx + 1 !== total) {
|
||||
style.marginBottom = '2px';
|
||||
}
|
||||
return (
|
||||
<div key={'file_' + idx} style={{...style}}>
|
||||
<CheckBox checked={pinned}
|
||||
changed={() => {
|
||||
const items = JSON.parse(JSON.stringify(this.state.items));
|
||||
const pinned = items[item_idx].meta.pinned = !items[item_idx].meta.pinned;
|
||||
this.setState({
|
||||
items
|
||||
}, () => {
|
||||
this.sendSyncRequest(Constants.IPC_Set_Pinned, {
|
||||
Provider: this.props.DisplayConfiguration,
|
||||
Remote: this.props.DisplayRemoteConfiguration,
|
||||
S3: this.props.DisplayS3Configuration,
|
||||
Version: this.props.version,
|
||||
Path: path,
|
||||
Pinned: pinned,
|
||||
});
|
||||
});
|
||||
}}
|
||||
label={name}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let idx = 0;
|
||||
return (
|
||||
<Box dxDark dxStyle={{
|
||||
height: 'calc(100vh - (var(--default_spacing) * 4)',
|
||||
padding: 'var(--default_spacing)',
|
||||
width: 'calc(100vw - (var(--default_spacing) * 4)'
|
||||
}}>
|
||||
<div className={'PinnedManager'}>
|
||||
<div className={'PinnedManagerHeading'}>
|
||||
<div className={'PinnedManagerClose'}>
|
||||
<a href={'#'}
|
||||
onClick={() => this.props.displayPinnedManager(false)}
|
||||
style={{cursor: 'pointer', flex: '0'}}>X</a>
|
||||
</div>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>{'Pinned File Manager'}</h1>
|
||||
<div className={'PinnedManagerActiveDirectory'}>
|
||||
<b> {this.state.active_directory}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'PinnedManagerItemsOwner'}>
|
||||
<div className={'PinnedManagerItems'}>
|
||||
{
|
||||
this.state.items.map((i, k) => {
|
||||
return i.directory ?
|
||||
this.createDirectory(i.name, i.path, idx++, this.state.items.length, k) :
|
||||
this.createFile(i.name, i.path, i.meta.pinned, idx++, this.state.items.length, k);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
});
|
||||
21
src/containers/SkynetExport/SkynetExport.css
Normal file
21
src/containers/SkynetExport/SkynetExport.css
Normal file
@@ -0,0 +1,21 @@
|
||||
.SkynetExportHeading {
|
||||
text-align: center;
|
||||
padding-bottom: var(--default_spacing);
|
||||
}
|
||||
|
||||
.SkynetExportList {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
height: calc(90vh - 80px);
|
||||
}
|
||||
|
||||
.SkynetExportTree {
|
||||
display: inline-flex;
|
||||
overflow-x: scroll;
|
||||
overflow-y: scroll;
|
||||
white-space: nowrap;
|
||||
height: calc(90vh - 80px);
|
||||
width: 100%;
|
||||
}
|
||||
235
src/containers/SkynetExport/SkynetExport.js
Normal file
235
src/containers/SkynetExport/SkynetExport.js
Normal file
@@ -0,0 +1,235 @@
|
||||
import React from 'react';
|
||||
import './SkynetExport.css';
|
||||
import CheckboxTree from 'react-checkbox-tree';
|
||||
import {connect} from 'react-redux';
|
||||
import IPCContainer from '../IPCContainer/IPCContainer';
|
||||
import {notifyApplicationBusy} from '../../redux/actions/common_actions';
|
||||
import {notifyError, notifyInfo} from '../../redux/actions/error_actions';
|
||||
import Box from '../../components/UI/Box/Box';
|
||||
import {displaySkynetExport} from '../../redux/actions/skynet_actions';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faCheckSquare, faChevronDown,
|
||||
faChevronRight, faFile, faFolder, faFolderOpen,
|
||||
faHSquare, faMinusSquare, faPlusSquare,
|
||||
faSquare
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import Button from '../../components/UI/Button/Button';
|
||||
|
||||
const Constants = require('../../constants');
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
AppBusy: state.common.AppBusy,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
displaySkynetExport: display => dispatch(displaySkynetExport(display)),
|
||||
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
|
||||
notifyError: msg => dispatch(notifyError(msg)),
|
||||
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer {
|
||||
state = {
|
||||
checked: [],
|
||||
clicked: {},
|
||||
expanded: [],
|
||||
nodes: [],
|
||||
second_stage: false,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setRequestHandler(Constants.IPC_Grab_Skynet_Tree_Reply, this.onGrabSkynetTreeReply);
|
||||
this.setRequestHandler(Constants.IPC_Export_Skylinks_Reply, this.onExportSkylinksReply);
|
||||
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
|
||||
createNodes = items => {
|
||||
/*
|
||||
{
|
||||
name: '',
|
||||
path: '',
|
||||
directory: true/false,
|
||||
children: [],
|
||||
}
|
||||
*/
|
||||
const ret = [];
|
||||
for (const item of items) {
|
||||
const treeItem = {
|
||||
label: item.name,
|
||||
path: item.path,
|
||||
value: item.name === '/' ? 0 : item.path ? item.path : JSON.stringify(item),
|
||||
};
|
||||
|
||||
if (item.directory) {
|
||||
treeItem.children = this.createNodes(item.children);
|
||||
}
|
||||
|
||||
ret.push(treeItem);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
handleNavigation = () => {
|
||||
if (this.state.second_stage) {
|
||||
this.props.notifyApplicationBusy(true);
|
||||
this.sendRequest(Constants.IPC_Export_Skylinks, {
|
||||
Version: this.props.version,
|
||||
Paths: this.state.checked,
|
||||
});
|
||||
} else {
|
||||
if (this.state.checked.length === 0) {
|
||||
this.props.notifyError('No files have been checked');
|
||||
} else {
|
||||
this.setState({
|
||||
second_stage: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExportSkylinksReply = (_, arg) => {
|
||||
this.props.notifyApplicationBusy(false);
|
||||
if (arg.data.Success) {
|
||||
this.setState({
|
||||
checked: [],
|
||||
clicked: {},
|
||||
expanded: [],
|
||||
nodes: [],
|
||||
second_stage: false,
|
||||
}, () => {
|
||||
this.props.notifyInfo('Skylink Exports', '!alternate!!copyable!' + JSON.stringify(arg.data.Result.success, null, 2));
|
||||
this.sendRequest(Constants.IPC_Grab_Skynet_Tree, {Version: this.props.version});
|
||||
});
|
||||
} else {
|
||||
this.props.notifyError(arg.data.Error);
|
||||
}
|
||||
}
|
||||
|
||||
onGrabSkynetTreeReply = (_, arg) => {
|
||||
if (arg.data.Success) {
|
||||
this.setState({
|
||||
checked: [],
|
||||
expanded: [0],
|
||||
nodes: this.createNodes(arg.data.Result),
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
checked: [],
|
||||
expanded: [],
|
||||
nodes: [],
|
||||
}, () => {
|
||||
this.props.notifyError(arg.data.Error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.AppBusy ? (<div/>) : (
|
||||
<Box dxDark dxStyle={{
|
||||
height: '90vh',
|
||||
padding: 'var(--default_spacing)',
|
||||
width: 'calc(100vw - (var(--default_spacing) * 4)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
float: 'right',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
marginTop: '-4px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'block'
|
||||
}}>
|
||||
<a href={'#'}
|
||||
onClick={() => this.props.displaySkynetExport(false)}
|
||||
style={{cursor: 'pointer'}}>X</a>
|
||||
</div>
|
||||
<h1
|
||||
className={'SkynetExportHeading'}>{this.state.second_stage ? 'Verify Exports' : 'Export Files'}</h1>
|
||||
<div className={this.state.second_stage ? 'SkynetExportList' : 'SkynetExportTree'}>
|
||||
{
|
||||
this.state.second_stage ?
|
||||
this.state.checked.map(path => {
|
||||
return (
|
||||
<input readOnly
|
||||
className={'ConfigurationItemInput'}
|
||||
key={path}
|
||||
style={{width: '100%', marginBottom: 'var(--default_spacing)'}}
|
||||
type={'text'}
|
||||
value={path}/>
|
||||
);
|
||||
})
|
||||
: (
|
||||
<CheckboxTree checked={this.state.checked}
|
||||
expanded={this.state.expanded}
|
||||
expandOnClick
|
||||
showExpandAll
|
||||
icons={{
|
||||
check: <FontAwesomeIcon icon={faCheckSquare} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
uncheck: <FontAwesomeIcon icon={faSquare} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
halfCheck: <FontAwesomeIcon icon={faHSquare} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
expandClose: <FontAwesomeIcon icon={faChevronRight} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
expandOpen: <FontAwesomeIcon icon={faChevronDown} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
expandAll: <FontAwesomeIcon icon={faPlusSquare} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
collapseAll: <FontAwesomeIcon icon={faMinusSquare} fixedWidth
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
parentClose: <FontAwesomeIcon icon={faFolder}
|
||||
fixedWidth
|
||||
color={'var(--heading_text_color)'}
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
parentOpen: <FontAwesomeIcon icon={faFolderOpen}
|
||||
fixedWidth
|
||||
color={'var(--heading_text_color)'}
|
||||
style={{padding: 0, margin: 0}}/>,
|
||||
leaf: <FontAwesomeIcon icon={faFile}
|
||||
fixedWidth
|
||||
color={'var(--text_color)'}
|
||||
style={{padding: 0, margin: 0}}/>
|
||||
}}
|
||||
nodes={this.state.nodes}
|
||||
onClick={clicked => this.setState({clicked})}
|
||||
onCheck={checked => this.setState({checked})}
|
||||
onExpand={expanded => this.setState({expanded})}/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div style={{display: 'flex', justifyContent: 'flex-end'}}>
|
||||
{
|
||||
this.state.second_stage ?
|
||||
<Button buttonStyles={{
|
||||
height: 'auto',
|
||||
marginLeft: 'var(--default_spacing)',
|
||||
marginTop: 'var(--default_spacing)',
|
||||
width: 'auto'
|
||||
}} clicked={() => this.setState({
|
||||
second_stage: false,
|
||||
})}>{'Back'}</Button> :
|
||||
null
|
||||
}
|
||||
<Button buttonStyles={{
|
||||
height: 'auto',
|
||||
marginLeft: 'var(--default_spacing)',
|
||||
marginTop: 'var(--default_spacing)',
|
||||
width: 'auto'
|
||||
}}
|
||||
clicked={this.handleNavigation}>{this.state.second_stage ? 'Export' : 'Next'}</Button>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
7
src/containers/SkynetImport/ImportList/Import/Import.css
Normal file
7
src/containers/SkynetImport/ImportList/Import/Import.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.ImportOwner {
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
margin: 0;
|
||||
padding-bottom: var(--default_spacing);
|
||||
}
|
||||
24
src/containers/SkynetImport/ImportList/Import/Import.js
Normal file
24
src/containers/SkynetImport/ImportList/Import/Import.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import './Import.css'
|
||||
|
||||
export default ({data}) => {
|
||||
return (
|
||||
<div className={'ImportOwner'}>
|
||||
<input readOnly
|
||||
className={'ConfigurationItemInput'}
|
||||
style={{maxWidth: 'calc(33.33% - var(--default_spacing)', marginRight: 'var(--default_spacing)'}}
|
||||
type={'text'}
|
||||
value={data.directory}/>
|
||||
<input readOnly
|
||||
className={'ConfigurationItemInput'}
|
||||
style={{maxWidth: 'calc(33.33% - calc(var(--default_spacing) / 2))'}}
|
||||
type={'text'}
|
||||
value={data.skylink}/>
|
||||
<input readOnly
|
||||
className={'ConfigurationItemInput'}
|
||||
style={{maxWidth: 'calc(33.33% - calc(var(--default_spacing) / 2))', marginLeft: 'var(--default_spacing)'}}
|
||||
type={'text'}
|
||||
value={data.token}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
13
src/containers/SkynetImport/ImportList/ImportList.css
Normal file
13
src/containers/SkynetImport/ImportList/ImportList.css
Normal file
@@ -0,0 +1,13 @@
|
||||
.ImportListOwner {
|
||||
max-height: 56vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ImportListHeader {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
29
src/containers/SkynetImport/ImportList/ImportList.js
Normal file
29
src/containers/SkynetImport/ImportList/ImportList.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import './ImportList.css'
|
||||
import Import from './Import/Import'
|
||||
import Text from '../../../components/UI/Text/Text';
|
||||
|
||||
export default ({imports_array}) => {
|
||||
let key = 0;
|
||||
return (
|
||||
<div>
|
||||
<div className={'ImportListHeader'}>
|
||||
<Text type={'Heading1'} text={'Directory'} style={{minWidth: '33.33%', maxWidth: '33.33%'}}/>
|
||||
<Text type={'Heading1'} text={'Skylink'} style={{minWidth: '33.33%', maxWidth: '33.33%'}}/>
|
||||
<Text type={'Heading1'} text={'Token'} style={{minWidth: '33.33%', maxWidth: '33.33%'}}/>
|
||||
</div>
|
||||
<hr/>
|
||||
<div className={'ImportListOwner'}>
|
||||
{
|
||||
imports_array.map(data => {
|
||||
return (
|
||||
<div key={'import_' + key++}>
|
||||
<Import data={data}/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
31
src/containers/SkynetImport/SkynetImport.css
Normal file
31
src/containers/SkynetImport/SkynetImport.css
Normal file
@@ -0,0 +1,31 @@
|
||||
.SkynetImportTextArea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
border-radius: var(--border_radius);
|
||||
background: var(--control_background);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
color: var(--text_color);
|
||||
box-sizing: border-box;
|
||||
resize: none;
|
||||
overflow-y: scroll;
|
||||
overflow: -moz-scrollbars-horizontal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.SkynetImportButtons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.SkynetActionButtons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.SkynetImportHeading {
|
||||
text-align: center;
|
||||
padding-bottom: var(--default_spacing);
|
||||
}
|
||||
228
src/containers/SkynetImport/SkynetImport.js
Normal file
228
src/containers/SkynetImport/SkynetImport.js
Normal file
@@ -0,0 +1,228 @@
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux';
|
||||
import './SkynetImport.css'
|
||||
import Box from '../../components/UI/Box/Box';
|
||||
import Button from '../../components/UI/Button/Button';
|
||||
import {displaySkynetImport} from '../../redux/actions/skynet_actions';
|
||||
import ImportList from './ImportList/ImportList'
|
||||
import IPCContainer from '../IPCContainer/IPCContainer';
|
||||
import {notifyApplicationBusy} from '../../redux/actions/common_actions';
|
||||
import {
|
||||
notifyError,
|
||||
notifyInfo
|
||||
} from '../../redux/actions/error_actions';
|
||||
|
||||
const Constants = require('../../constants');
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
AppBusy: state.common.AppBusy,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
displaySkynetImport: display => dispatch(displaySkynetImport(display)),
|
||||
notifyApplicationBusy: busy => dispatch(notifyApplicationBusy(busy, true)),
|
||||
notifyError: msg => dispatch(notifyError(msg)),
|
||||
notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)),
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class extends IPCContainer {
|
||||
|
||||
state = {
|
||||
import_text: '',
|
||||
imports_array: [],
|
||||
second_stage: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.setRequestHandler(Constants.IPC_Import_Skylinks_Reply, this.onImportSkylinksReply);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
|
||||
displaySyntax = () => {
|
||||
const msg = '!alternate!To import Skylinks into the root directory, list each Skylink on a new line:\n' +
|
||||
' AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA\n' +
|
||||
' AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg\n' +
|
||||
'\n' +
|
||||
'To import Skylinks into new or existing directories, use the following syntax:\n' +
|
||||
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA"\n' +
|
||||
' directory="/my/sub/dir2",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg"\n' +
|
||||
'\n' +
|
||||
'You can also specify a password if this in an encrypted Repertory Skylink:\n' +
|
||||
' directory="/my/sub/dir",skylink="AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",token="My Password"\n' +
|
||||
' directory="/my/sub/dir",skylink="AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",token="My Password"\n' +
|
||||
'\n' +
|
||||
'To import Skylinks using JSON syntax:\n' +
|
||||
' [\n' +
|
||||
' {\n' +
|
||||
' "directory": "/",\n' +
|
||||
' "skylink": "AACeCiD6WQG6DzDcCdIu3cFPSxMUMoQPx46NYSyijNMKUA",\n' +
|
||||
' "token": "My Password"\n' +
|
||||
' },\n' +
|
||||
' {\n' +
|
||||
' "directory": "/my/sub/dir",\n' +
|
||||
' "skylink": "AACyjmDGoHqY7mTaxi-rkpnKUJGZK1B4UhrF74Nv6tY6Cg",\n' +
|
||||
' "token": "My Password"\n' +
|
||||
' }\n' +
|
||||
' ]';
|
||||
this.props.notifyInfo('Import Syntax', msg)
|
||||
}
|
||||
|
||||
handleNavigation = () => {
|
||||
if (this.state.second_stage) {
|
||||
try {
|
||||
this.props.notifyApplicationBusy(true);
|
||||
this.sendRequest(Constants.IPC_Import_Skylinks, {
|
||||
Version: this.props.version,
|
||||
JsonArray: this.state.imports_array,
|
||||
});
|
||||
} catch (e) {
|
||||
this.props.notifyApplicationBusy(false);
|
||||
this.props.notifyError(e);
|
||||
}
|
||||
} else {
|
||||
const items = this.state.import_text.split('\n');
|
||||
let importsArray = [];
|
||||
try {
|
||||
for (let item of items) {
|
||||
item = item.trim();
|
||||
if (item.startsWith('[')) {
|
||||
importsArray = JSON.parse(this.state.import_text.trim());
|
||||
break;
|
||||
} else if (item.indexOf('=') >= 0) {
|
||||
const parts = item.split(',')
|
||||
let importItem = {
|
||||
directory: '/',
|
||||
skylink: '',
|
||||
token: '',
|
||||
};
|
||||
for (let part of parts) {
|
||||
part = part.trim();
|
||||
const pair = part.split('=');
|
||||
if (pair.length !== 2) {
|
||||
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
|
||||
}
|
||||
importItem = {
|
||||
...importItem,
|
||||
[pair[0].trim()]: pair[1].trim().replace(/"/g, ''),
|
||||
};
|
||||
}
|
||||
if (!importItem.skylink) {
|
||||
throw new Error('Invalid syntax for import: directory="",skylink="",token=""');
|
||||
}
|
||||
importsArray.push(importItem);
|
||||
} else if (item.length > 0) {
|
||||
importsArray.push({
|
||||
directory: '/',
|
||||
skylink: item,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (importsArray.length === 0) {
|
||||
throw new Error('Nothing to import');
|
||||
}
|
||||
this.setState({
|
||||
imports_array: importsArray,
|
||||
second_stage: true,
|
||||
});
|
||||
} catch (e) {
|
||||
this.props.notifyError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onImportSkylinksReply = (_, arg) => {
|
||||
this.props.notifyApplicationBusy(false);
|
||||
if (arg.data.Success) {
|
||||
const failedImportsArray = this.state.imports_array.filter(i => {
|
||||
return arg.data.Result.failed.includes(i.skylink);
|
||||
});
|
||||
const count = this.state.imports_array.length;
|
||||
this.setState({
|
||||
import_text: failedImportsArray.length > 0 ? JSON.stringify(failedImportsArray, null, 2) : '',
|
||||
imports_array: [],
|
||||
second_stage: false,
|
||||
}, () => {
|
||||
if (failedImportsArray.length > 0) {
|
||||
this.props.notifyError(`Failed to import ${failedImportsArray.length} item(s)`);
|
||||
} else {
|
||||
this.props.notifyInfo('Import Result', `Successfully imported ${count} item(s)`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.props.notifyError(arg.data.Error);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return this.props.AppBusy ? (<div/>) : (
|
||||
<Box dxDark dxStyle={{
|
||||
height: 'auto',
|
||||
padding: 'var(--default_spacing)',
|
||||
width: 'calc(100vw - (var(--default_spacing) * 4)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
float: 'right',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
marginTop: '-4px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'block'
|
||||
}}>
|
||||
<a href={'#'}
|
||||
onClick={() => this.props.displaySkynetImport(false)}
|
||||
style={{cursor: 'pointer'}}>X</a>
|
||||
</div>
|
||||
<h1
|
||||
className={'SkynetImportHeading'}>{this.state.second_stage ? 'Verify Imports' : 'Import List'}</h1>
|
||||
{
|
||||
this.state.second_stage ? (
|
||||
<ImportList imports_array={this.state.imports_array}/>
|
||||
) : (
|
||||
<textarea autoFocus={true}
|
||||
className={'SkynetImportTextArea'}
|
||||
onChange={e => this.setState({
|
||||
import_text: e.target.value,
|
||||
})}
|
||||
value={this.state.import_text}
|
||||
rows={10}/>
|
||||
)
|
||||
}
|
||||
<div className={'SkynetImportButtons'}>
|
||||
<Button
|
||||
buttonStyles={{height: 'auto', marginTop: 'var(--default_spacing)', width: 'auto'}}
|
||||
clicked={this.displaySyntax}>Import Syntax...</Button>
|
||||
<div className={'SkynetActionButtons'}>
|
||||
{
|
||||
this.state.second_stage ?
|
||||
<Button buttonStyles={{
|
||||
height: 'auto',
|
||||
marginLeft: 'var(--default_spacing)',
|
||||
marginTop: 'var(--default_spacing)',
|
||||
width: 'auto'
|
||||
}} clicked={() => this.setState({
|
||||
second_stage: false,
|
||||
})}>{'Back'}</Button> :
|
||||
null
|
||||
}
|
||||
<Button buttonStyles={{
|
||||
height: 'auto',
|
||||
marginLeft: 'var(--default_spacing)',
|
||||
marginTop: 'var(--default_spacing)',
|
||||
width: 'auto'
|
||||
}}
|
||||
clicked={this.handleNavigation}>{this.state.second_stage ? 'Import' : 'Next'}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
34
src/containers/UI/Password/Password.css
Normal file
34
src/containers/UI/Password/Password.css
Normal file
@@ -0,0 +1,34 @@
|
||||
.PasswordOwner {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-content: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
input.PasswordInput {
|
||||
display: block;
|
||||
margin-right: var(--default_spacing);
|
||||
padding: 2px;
|
||||
border-radius: var(--border_radius);
|
||||
background: var(--control_background);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
color: var(--text_color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.PasswordShowHide {
|
||||
padding-top: 2px;
|
||||
cursor: pointer;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.PasswordLink {
|
||||
margin-right: var(--default_spacing);
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
}
|
||||
135
src/containers/UI/Password/Password.js
Normal file
135
src/containers/UI/Password/Password.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import React, {Component} from 'react';
|
||||
import './Password.css';
|
||||
import {faEye, faEyeSlash} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
|
||||
export default class extends Component {
|
||||
state = {
|
||||
button_text: 'clear',
|
||||
password: '',
|
||||
password2: '',
|
||||
show_password: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.value || (this.props.value.length === 0)) {
|
||||
this.setState({
|
||||
...this.state,
|
||||
button_text: 'set',
|
||||
password: '',
|
||||
password2: '',
|
||||
show_password: false,
|
||||
})
|
||||
} else {
|
||||
this.setState({
|
||||
...this.state,
|
||||
button_text: 'clear',
|
||||
password: this.props.value,
|
||||
password2: '',
|
||||
show_password: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleActionClick = () => {
|
||||
if (!this.props.disabled) {
|
||||
switch (this.state.button_text) {
|
||||
case 'clear':
|
||||
this.setState({
|
||||
...this.state,
|
||||
button_text: 'set',
|
||||
password: '',
|
||||
password2: '',
|
||||
});
|
||||
break;
|
||||
|
||||
case 'confirm':
|
||||
if (this.state.password === this.state.password2) {
|
||||
this.props.changed(this.state.password);
|
||||
this.setState({
|
||||
...this.state,
|
||||
button_text: this.state.password && this.state.password.length > 0 ? 'clear' : 'set',
|
||||
password2: '',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
...this.state,
|
||||
button_text: 'set',
|
||||
password: '',
|
||||
password2: '',
|
||||
}, () => {
|
||||
if (this.props.mismatchHandler) {
|
||||
this.props.mismatchHandler();
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'set':
|
||||
this.setState({
|
||||
...this.state,
|
||||
button_text: 'confirm',
|
||||
password2: '',
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePasswordChanged = e => {
|
||||
if (this.state.button_text === 'set') {
|
||||
this.setState({
|
||||
...this.state,
|
||||
password: e.target.value,
|
||||
});
|
||||
} else if (this.state.button_text === 'confirm') {
|
||||
this.setState({
|
||||
...this.state,
|
||||
password2: e.target.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handlePasswordKeyUp = e => {
|
||||
if ((e.keyCode === 13) && ((this.state.button_text === 'confirm') || (this.state.button_text === 'set'))) {
|
||||
this.handleActionClick();
|
||||
}
|
||||
};
|
||||
|
||||
handleShowHideClick = () => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
show_password: !this.state.show_password,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={'PasswordOwner'} style={{...this.props.style}}>
|
||||
{
|
||||
this.props.readOnly ? null : <a href={'#'}
|
||||
className={'PasswordLink'}
|
||||
onClick={this.handleActionClick}>
|
||||
<u>{this.state.button_text}</u>
|
||||
</a>
|
||||
}
|
||||
<input autoFocus={this.props.autoFocus}
|
||||
readOnly={this.props.readOnly}
|
||||
className={'PasswordInput'}
|
||||
disabled={this.props.readOnly || (this.state.button_text === 'clear')}
|
||||
onChange={this.handlePasswordChanged}
|
||||
onKeyUp={this.handlePasswordKeyUp}
|
||||
type={this.state.show_password ? 'text' : 'password'}
|
||||
value={(this.state.button_text === 'confirm') ? this.state.password2 : this.state.password}/>
|
||||
<a href={'#'}
|
||||
className={'PasswordShowHide'}
|
||||
onClick={this.handleShowHideClick}>
|
||||
<FontAwesomeIcon icon={this.state.show_password ? faEye : faEyeSlash} fixedWidth/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
1044
src/helpers.js
1044
src/helpers.js
File diff suppressed because it is too large
Load Diff
@@ -8,11 +8,11 @@
|
||||
--control_transparent_background: rgba(10, 10, 16, 0.5);
|
||||
--control_dark_transparent_background: rgba(10, 10, 16, 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: rgba(200, 200, 240, 0.65);
|
||||
--text_color_hover: rgba(200, 200, 225, 0.65);
|
||||
--text_color_error: rgba(203, 120, 120, 0.8);
|
||||
--heading_text_color: rgba(132, 160, 230, 0.8);
|
||||
--heading_other_text_color: rgba(132, 160, 230, 0.65);
|
||||
--text_color_transition: color 0.3s;
|
||||
|
||||
--default_font_size: 14px;
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
a {
|
||||
outline: 0;
|
||||
color: var(--text_color);
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
html, body {
|
||||
@@ -80,6 +83,7 @@ p {
|
||||
|
||||
.scrollable-content, ::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.scrollable-content, ::-webkit-scrollbar * {
|
||||
@@ -89,3 +93,7 @@ p {
|
||||
.scrollable-content, ::-webkit-scrollbar-thumb {
|
||||
background: var(--control_background_hover) !important;
|
||||
}
|
||||
|
||||
.scrollbar-corner, ::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
27
src/index.js
27
src/index.js
@@ -1,14 +1,18 @@
|
||||
import './index.css';
|
||||
import 'react-checkbox-tree/lib/react-checkbox-tree.css';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import createAppStore from './redux/store/createAppStore';
|
||||
import {getIPCRenderer} from './utils';
|
||||
import packageJson from '../package.json';
|
||||
import {Provider} from 'react-redux';
|
||||
import {setActiveRelease} from './redux/actions/release_version_actions';
|
||||
|
||||
import packageJson from '../package.json';
|
||||
|
||||
import App from './App.jsx';
|
||||
import {setProviderState} from './redux/actions/mount_actions';
|
||||
import {setActiveRelease} from './redux/actions/release_version_actions';
|
||||
import createAppStore from './redux/store/createAppStore';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import {getIPCRenderer} from './utils';
|
||||
|
||||
const Constants = require('./constants');
|
||||
|
||||
@@ -16,22 +20,22 @@ const ipcRenderer = getIPCRenderer();
|
||||
let store;
|
||||
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.once(Constants.IPC_Get_Platform_Reply, (event, platformInfo) => {
|
||||
ipcRenderer.once(Constants.IPC_Get_Platform_Reply, (_, platformInfo) => {
|
||||
if (platformInfo.Platform === 'linux') {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--default_font_size', '15.3px');
|
||||
}
|
||||
|
||||
ipcRenderer.once(Constants.IPC_Get_State_Reply, (event, result) => {
|
||||
ipcRenderer.once(Constants.IPC_Get_State_Reply, (_, 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,
|
||||
...store.getState().mounts.S3Mounts,
|
||||
];
|
||||
for (const provider of providerList) {
|
||||
let state = result.data[provider] || store.getState().mounts.ProviderState[provider];
|
||||
const state = result.data[provider];
|
||||
if (state.AutoMount === undefined) {
|
||||
state['AutoMount'] = false;
|
||||
}
|
||||
@@ -40,6 +44,8 @@ if (ipcRenderer) {
|
||||
}
|
||||
store.dispatch(setProviderState(provider, state));
|
||||
}
|
||||
store.dispatch(
|
||||
setActiveRelease(result.data.Release, result.data.Version));
|
||||
} else {
|
||||
store = createAppStore(platformInfo, packageJson.version, {});
|
||||
}
|
||||
@@ -55,4 +61,3 @@ if (ipcRenderer) {
|
||||
});
|
||||
ipcRenderer.send(Constants.IPC_Get_Platform);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,17 @@ const handleConfirmYesNo = (show, titleOrConfirmed, resolve) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const NOTIFY_APPLICATION_BUSY = 'common/notifyApplicationBusy';
|
||||
export const notifyApplicationBusy = (busy, transparent) => {
|
||||
return {
|
||||
type: NOTIFY_APPLICATION_BUSY,
|
||||
payload: {
|
||||
busy,
|
||||
transparent
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const notifyRebootRequired = createAction('common/notifyRebootRequired');
|
||||
|
||||
export const rebootSystem = () => {
|
||||
|
||||
@@ -5,14 +5,16 @@ import {
|
||||
getSelectedVersionFromState
|
||||
} from '../../utils';
|
||||
import {notifyError} from './error_actions';
|
||||
import {setAllowDownload} from './download_actions';
|
||||
import {downloadItem, setAllowDownload} from './download_actions';
|
||||
import {
|
||||
loadReleases,
|
||||
setActiveRelease,
|
||||
setInstalledVersion,
|
||||
setReleaseUpgradeAvailable
|
||||
setReleaseUpgradeAvailable,
|
||||
setNewReleasesAvailable2,
|
||||
} from './release_version_actions';
|
||||
import {
|
||||
confirmYesNo,
|
||||
displaySelectAppPlatform,
|
||||
setAllowMount,
|
||||
setApplicationReady,
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
showWindow,
|
||||
shutdownApplication
|
||||
} from './common_actions';
|
||||
import {unmountAll} from './mount_actions';
|
||||
|
||||
const ipcRenderer = getIPCRenderer();
|
||||
|
||||
@@ -32,13 +35,15 @@ export const checkInstalled = (dependencies, version) => {
|
||||
const installedVersion = result.Success && result.Exists ? result.Version : 'none';
|
||||
const state = getState();
|
||||
|
||||
const release = state.relver.Release;
|
||||
let version = state.relver.Version;
|
||||
|
||||
let upgradeAvailable = false;
|
||||
if (installedVersion !== 'none') {
|
||||
const latestVersion = state.relver.VersionLookup[Constants.RELEASE_TYPES[state.relver.Release]].length - 1;
|
||||
let version = state.relver.Version;
|
||||
const latestVersion = state.relver.VersionLookup[Constants.RELEASE_TYPES[release]].length - 1;
|
||||
if (version === -1) {
|
||||
version = latestVersion;
|
||||
dispatch(setActiveRelease(state.relver.Release, version));
|
||||
dispatch(setActiveRelease(release, version));
|
||||
} else {
|
||||
upgradeAvailable = version !== latestVersion;
|
||||
}
|
||||
@@ -48,8 +53,18 @@ export const checkInstalled = (dependencies, version) => {
|
||||
dispatch(setMissingDependencies(result.Dependencies));
|
||||
dispatch(setAllowDownload(true));
|
||||
dispatch(setAllowMount(true));
|
||||
|
||||
const autoInstallRelease = getState().install.AutoInstallRelease;
|
||||
dispatch(setAutoInstallRelease(false));
|
||||
|
||||
if (result.Dependencies && (result.Dependencies.length > 0)) {
|
||||
dispatch(showWindow());
|
||||
} else if ((installedVersion === 'none') && autoInstallRelease) {
|
||||
dispatch(setAllowMount(false));
|
||||
const versionString = getState().relver.VersionLookup[Constants.RELEASE_TYPES[release]][version];
|
||||
const urls = getState().relver.LocationsLookup[versionString].urls;
|
||||
const fileName = versionString + '.zip';
|
||||
dispatch(downloadItem(fileName, Constants.INSTALL_TYPES.Release, urls));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -140,7 +155,7 @@ export const installDependency = (source, url, isWinFSP) => {
|
||||
export const installAndTestRelease = (source, version, appPlatform) => {
|
||||
return (dispatch, getState) => {
|
||||
if (ipcRenderer && getState().install.InstallTestActive) {
|
||||
const extractReleaseComplete = (event, arg) => {
|
||||
const extractReleaseComplete = () => {
|
||||
ipcRenderer.send(Constants.IPC_Delete_File, {
|
||||
FilePath: source,
|
||||
});
|
||||
@@ -174,6 +189,31 @@ export const installAndTestRelease = (source, version, appPlatform) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const installReleaseByVersion = (release, version) => {
|
||||
return (dispatch, getState) => {
|
||||
const install = () => {
|
||||
if (getState().download.AllowDownload && !getState().download.DownloadActive) {
|
||||
dispatch(setAutoInstallRelease(true));
|
||||
dispatch(setActiveRelease(release, version));
|
||||
} else {
|
||||
notifyError('Download is active. Unable to install release.');
|
||||
}
|
||||
};
|
||||
|
||||
if (getState().mounts.MountsBusy) {
|
||||
dispatch(confirmYesNo('Unmount all drives?'))
|
||||
.then(confirmed => {
|
||||
if (confirmed) {
|
||||
dispatch(unmountAll(install));
|
||||
}
|
||||
})
|
||||
.catch(error => notifyError(error));
|
||||
} else {
|
||||
install();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const installRelease = source => {
|
||||
return (dispatch, getState) => {
|
||||
if (ipcRenderer && !getState().install.InstallActive) {
|
||||
@@ -185,6 +225,11 @@ export const installRelease = source => {
|
||||
FilePath: source,
|
||||
});
|
||||
|
||||
if (arg.data.Success) {
|
||||
localStorage.setItem('previous_releases', localStorage.getItem('releases'));
|
||||
dispatch(setNewReleasesAvailable2([]));
|
||||
}
|
||||
|
||||
dispatch(setInstallComplete(arg.data));
|
||||
dispatch(checkVersionInstalled());
|
||||
};
|
||||
@@ -231,6 +276,7 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const setAutoInstallRelease = createAction('install/setAutoInstallRelease');
|
||||
export const setDismissDependencies = createAction('install/setDismissDependencies');
|
||||
export const setInstallActive = createAction('install/setInstallActive');
|
||||
export const setInstallTestActive = createAction('install/setInstallTestActive');
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as Constants from '../../constants';
|
||||
import {createAction} from '@reduxjs/toolkit';
|
||||
|
||||
import * as Constants from '../../constants';
|
||||
import {getIPCRenderer} from '../../utils';
|
||||
import {
|
||||
confirmYesNo,
|
||||
saveState
|
||||
} from './common_actions';
|
||||
|
||||
import {confirmYesNo, saveState} from './common_actions';
|
||||
import {notifyError} from './error_actions';
|
||||
|
||||
export const addRemoteMount = (hostNameOrIp, port, token) => {
|
||||
@@ -12,18 +11,20 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
|
||||
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) {
|
||||
dispatch(addRemoteMount2(provider));
|
||||
ipcRenderer.send(Constants.IPC_Detect_Mount, {
|
||||
Provider: provider,
|
||||
RemoteMounts: getState().mounts.RemoteMounts,
|
||||
S3Mounts: getState().mounts.S3Mounts,
|
||||
Version: getState().relver.InstalledVersion,
|
||||
});
|
||||
} else {
|
||||
dispatch(notifyError('Failed to set \'RemoteToken\': ' + arg.data.Error));
|
||||
dispatch(
|
||||
notifyError('Failed to set \'RemoteToken\': ' + arg.data.Error));
|
||||
dispatch(setBusy(false));
|
||||
}
|
||||
});
|
||||
@@ -42,112 +43,129 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const addS3Mount = (name, accessKey, secretKey, region, bucketName, url) => {
|
||||
return (dispatch, getState) => {
|
||||
const ipcRenderer = getIPCRenderer();
|
||||
const provider = 'S3' + name;
|
||||
dispatch(setBusy(true));
|
||||
|
||||
ipcRenderer.once(Constants.IPC_Set_Config_Values_Reply, (_, arg) => {
|
||||
if (arg.data.Success) {
|
||||
dispatch(addS3Mount2(provider));
|
||||
ipcRenderer.send(Constants.IPC_Detect_Mount, {
|
||||
Provider: provider,
|
||||
RemoteMounts: getState().mounts.RemoteMounts,
|
||||
S3Mounts: getState().mounts.S3Mounts,
|
||||
Version: getState().relver.InstalledVersion,
|
||||
});
|
||||
} else {
|
||||
dispatch(
|
||||
notifyError('Failed to create S3 instance: ' + arg.data.Error));
|
||||
dispatch(setBusy(false));
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.send(Constants.IPC_Set_Config_Values, {
|
||||
Items: [
|
||||
{Name: 'S3Config.AccessKey', Value: accessKey},
|
||||
{Name: 'S3Config.SecretKey', Value: secretKey},
|
||||
{Name: 'S3Config.Region', Value: region},
|
||||
{Name: 'S3Config.BucketName', Value: bucketName},
|
||||
{Name: 'S3Config.URL', Value: url},
|
||||
],
|
||||
Provider: provider,
|
||||
S3: true,
|
||||
Version: getState().relver.InstalledVersion,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const addRemoteMount2 = createAction('mounts/addRemoteMount2');
|
||||
export const addS3Mount2 = createAction('mounts/addS3Mount2');
|
||||
|
||||
export const DISPLAY_CONFIGURATION = 'mounts/displayConfiguration';
|
||||
export const displayConfiguration = (provider, remote) => {
|
||||
export const displayConfiguration = (provider, remote, s3) => {
|
||||
return {
|
||||
type: DISPLAY_CONFIGURATION,
|
||||
payload: {
|
||||
provider,
|
||||
remote,
|
||||
s3,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const removeRemoteMount = provider => {
|
||||
export const removeMount = provider => {
|
||||
return dispatch => {
|
||||
dispatch(confirmYesNo('Delete [' + provider.substr(6) + ']?'))
|
||||
.then(confirmed => {
|
||||
if (confirmed) {
|
||||
dispatch(removeRemoteMount2(provider));
|
||||
}
|
||||
});
|
||||
const isRemote = provider.startsWith('Remote');
|
||||
dispatch(confirmYesNo('Delete [' + provider.substr(isRemote ? 6 : 2) + ']?'))
|
||||
.then(confirmed => {
|
||||
if (confirmed) {
|
||||
dispatch(removeMount2(provider));
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const removeRemoteMount2 = provider => {
|
||||
const removeMount2 = provider => {
|
||||
return dispatch => {
|
||||
const ipcRenderer = getIPCRenderer();
|
||||
ipcRenderer.once(Constants.IPC_Remove_Remote_Mount_Reply, (_, arg) => {
|
||||
ipcRenderer.once(Constants.IPC_Remove_Mount_Reply, (_, arg) => {
|
||||
if (arg.data.Success) {
|
||||
dispatch(removeRemoteMount3(provider));
|
||||
dispatch(removeMount3(provider));
|
||||
dispatch(saveState());
|
||||
}
|
||||
});
|
||||
ipcRenderer.send(Constants.IPC_Remove_Remote_Mount, provider.substr(6));
|
||||
const isRemote = provider.startsWith('Remote');
|
||||
ipcRenderer.send(Constants.IPC_Remove_Mount, {
|
||||
Remote: isRemote,
|
||||
Name: provider.substr(isRemote ? 6 : 2)
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const removeRemoteMount3 = createAction('mounts/removeRemoteMount3');
|
||||
export const removeMount3 = createAction('mounts/removeMount3');
|
||||
|
||||
export const RESET_MOUNTS_STATE = 'mounts/resetMountsState';
|
||||
export const resetMountsState = () => {
|
||||
return {
|
||||
type: RESET_MOUNTS_STATE,
|
||||
payload: null,
|
||||
}
|
||||
return {type: RESET_MOUNTS_STATE, payload: null,}
|
||||
};
|
||||
|
||||
export const SET_ALLOW_MOUNT = 'mounts/setAllowMount';
|
||||
export const setAllowMount = (provider, allow) => {
|
||||
return {
|
||||
type: SET_ALLOW_MOUNT,
|
||||
payload: {
|
||||
provider,
|
||||
allow
|
||||
}
|
||||
export const setAllowMount =
|
||||
(provider,
|
||||
allow) => {
|
||||
return {type: SET_ALLOW_MOUNT, payload: {provider, allow}};
|
||||
};
|
||||
};
|
||||
|
||||
export const SET_AUTO_MOUNT_PROCESSED = 'mounts/setAutoMountProcessed';
|
||||
export const setAutoMountProcessed = (provider, processed) => {
|
||||
return {
|
||||
type: SET_AUTO_MOUNT_PROCESSED,
|
||||
payload: {
|
||||
provider,
|
||||
processed
|
||||
}
|
||||
};
|
||||
return {type: SET_AUTO_MOUNT_PROCESSED, payload: {provider, processed}};
|
||||
};
|
||||
|
||||
export const setBusy = createAction('mounts/setBusy');
|
||||
|
||||
export const SET_MOUNT_STATE = 'mounts/setMountState';
|
||||
export const setMountState = (provider, state) => {
|
||||
return {
|
||||
type: SET_MOUNT_STATE,
|
||||
payload: {
|
||||
provider,
|
||||
state
|
||||
}
|
||||
export const setMountState =
|
||||
(provider,
|
||||
state) => {
|
||||
return {type: SET_MOUNT_STATE, payload: {provider, state}};
|
||||
};
|
||||
};
|
||||
|
||||
export const SET_MOUNTED = 'mounts/setMounted';
|
||||
export const setMounted = (provider, mounted) => {
|
||||
return {
|
||||
type: SET_MOUNTED,
|
||||
payload: {
|
||||
provider,
|
||||
mounted
|
||||
}
|
||||
export const setMounted =
|
||||
(provider,
|
||||
mounted) => {
|
||||
return {type: SET_MOUNTED, payload: {provider, mounted}};
|
||||
};
|
||||
};
|
||||
|
||||
export const SET_PROVIDER_STATE = 'mounts/setProviderState';
|
||||
export const setProviderState = (provider, state) => {
|
||||
return {
|
||||
type: SET_PROVIDER_STATE,
|
||||
payload: {
|
||||
provider,
|
||||
state
|
||||
}
|
||||
}
|
||||
return {type: SET_PROVIDER_STATE, payload: {provider, state}}
|
||||
};
|
||||
|
||||
export const unmountAll = completedCallback => {
|
||||
return (dispatch, getState) => {
|
||||
return dispatch => {
|
||||
const ipcRenderer = getIPCRenderer();
|
||||
const unmountedCallback = () => {
|
||||
dispatch(resetMountsState());
|
||||
|
||||
4
src/redux/actions/pinned_manager_actions.js
Normal file
4
src/redux/actions/pinned_manager_actions.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import {createAction} from '@reduxjs/toolkit';
|
||||
|
||||
export const displayPinnedManager = createAction('pinned/displayPinnedManager');
|
||||
|
||||
@@ -13,7 +13,11 @@ import {
|
||||
setDismissDependencies
|
||||
} from './install_actions';
|
||||
import {unmountAll} from './mount_actions';
|
||||
import {getIPCRenderer} from '../../utils';
|
||||
import {
|
||||
checkNewReleases,
|
||||
getIPCRenderer,
|
||||
getNewReleases, getSelectedVersionFromState
|
||||
} from '../../utils';
|
||||
|
||||
export const CLEAR_UI_UPGRADE = 'relver/clearUIUpgrade';
|
||||
export const clearUIUpgrade = () => {
|
||||
@@ -67,13 +71,13 @@ export const loadReleases = () => {
|
||||
const state = getState().relver;
|
||||
let release = state.Release;
|
||||
if (release >= Constants.RELEASE_TYPES.length) {
|
||||
release = state.ReleaseDefault;
|
||||
release = Constants.DEFAULT_RELEASE;
|
||||
}
|
||||
|
||||
let latestVersion = versionLookup[Constants.RELEASE_TYPES[release]].length - 1;
|
||||
let version = state.Version;
|
||||
if (versionLookup[Constants.RELEASE_TYPES[release]][0] === 'unavailable') {
|
||||
release = state.ReleaseDefault;
|
||||
release = Constants.DEFAULT_RELEASE;
|
||||
version = latestVersion = versionLookup[Constants.RELEASE_TYPES[release]].length - 1
|
||||
} else if ((version === -1) || !versionLookup[Constants.RELEASE_TYPES[release]][version]) {
|
||||
version = latestVersion;
|
||||
@@ -123,11 +127,26 @@ export const loadReleases = () => {
|
||||
...response.data.Locations[appPlatform],
|
||||
};
|
||||
|
||||
const storedReleases = localStorage.getItem('releases');
|
||||
let newReleases = [];
|
||||
if (storedReleases && (storedReleases.length > 0)) {
|
||||
newReleases = getNewReleases(JSON.parse(storedReleases).VersionLookup, versionLookup, getSelectedVersionFromState(getState()));
|
||||
}
|
||||
|
||||
localStorage.setItem('releases', JSON.stringify({
|
||||
LocationsLookup: locationsLookup,
|
||||
VersionLookup: versionLookup
|
||||
}));
|
||||
dispatchActions(locationsLookup, versionLookup);
|
||||
|
||||
dispatch(setNewReleasesAvailable(newReleases));
|
||||
if (getState().relver.NewReleasesAvailable.length > 0) {
|
||||
dispatch(setNewReleasesAvailable2(newReleases));
|
||||
localStorage.setItem('previous_releases', storedReleases);
|
||||
dispatch(showWindow());
|
||||
} else if ((newReleases = checkNewReleases(getSelectedVersionFromState(getState()))).length > 0) {
|
||||
dispatch(setNewReleasesAvailable2(newReleases));
|
||||
}
|
||||
}).catch(error => {
|
||||
const releases = localStorage.getItem('releases');
|
||||
if (releases && (releases.length > 0)) {
|
||||
@@ -160,7 +179,7 @@ export const setActiveRelease = (release, version) => {
|
||||
const relver = getState().relver;
|
||||
const common = getState().common;
|
||||
if (release >= Constants.RELEASE_TYPES.length) {
|
||||
release = relver.ReleaseDefault;
|
||||
release = Constants.DEFAULT_RELEASE;
|
||||
version = -1;
|
||||
}
|
||||
const versions = relver.VersionLookup[Constants.RELEASE_TYPES[release]];
|
||||
@@ -174,8 +193,11 @@ export const setActiveRelease = (release, version) => {
|
||||
};
|
||||
|
||||
export const setAllowDismissDependencies = createAction('relver/setAllowDismissDependencies');
|
||||
export const setDismissNewReleasesAvailable = createAction('relver/setDismissNewReleasesAvailable');
|
||||
export const setDismissUIUpgrade = createAction('relver/setDismissUIUpgrade');
|
||||
export const setInstalledVersion = createAction('relver/setInstalledVersion');
|
||||
export const setNewReleasesAvailable = createAction('relver/setNewReleasesAvailable');
|
||||
export const setNewReleasesAvailable2 = createAction('relver/setNewReleasesAvailable2');
|
||||
|
||||
export const SET_RELEASE_DATA = 'relver/setReleaseData';
|
||||
export const setReleaseData = (locationsLookup, versionLookup)=> {
|
||||
|
||||
4
src/redux/actions/skynet_actions.js
Normal file
4
src/redux/actions/skynet_actions.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import {createAction} from '@reduxjs/toolkit';
|
||||
|
||||
export const displaySkynetExport = createAction('skynet/displaySkynetExport');
|
||||
export const displaySkynetImport = createAction('skynet/displaySkynetImport');
|
||||
@@ -1,6 +1,7 @@
|
||||
import {createReducer} from '@reduxjs/toolkit';
|
||||
import {
|
||||
DISPLAY_CONFIRM_YES_NO,
|
||||
NOTIFY_APPLICATION_BUSY,
|
||||
notifyRebootRequired,
|
||||
setAllowMount,
|
||||
setApplicationReady,
|
||||
@@ -11,6 +12,8 @@ import {
|
||||
export const createCommonReducer = (platformInfo, version) => {
|
||||
return createReducer({
|
||||
AllowMount: false,
|
||||
AppBusy: false,
|
||||
AppBusyTransparent: false,
|
||||
AppPlatform: platformInfo.AppPlatform,
|
||||
AppReady: false,
|
||||
DisplayConfirmYesNo: false,
|
||||
@@ -51,6 +54,13 @@ export const createCommonReducer = (platformInfo, version) => {
|
||||
AppPlatform: action.payload,
|
||||
}
|
||||
},
|
||||
[NOTIFY_APPLICATION_BUSY]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
AppBusy: action.payload.busy,
|
||||
AppBusyTransparent: action.payload.busy && action.payload.transparent,
|
||||
};
|
||||
},
|
||||
[notifyRebootRequired]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {createReducer} from '@reduxjs/toolkit';
|
||||
import {
|
||||
setAutoInstallRelease,
|
||||
setDismissDependencies,
|
||||
setInstallActive,
|
||||
setInstallComplete,
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
} from '../actions/install_actions';
|
||||
|
||||
export const installReducer = createReducer({
|
||||
AutoInstallRelease: false,
|
||||
DismissDependencies: false,
|
||||
InstallActive: false,
|
||||
InstallResult: null,
|
||||
@@ -15,6 +17,12 @@ export const installReducer = createReducer({
|
||||
InstallType: null,
|
||||
MissingDependencies: [],
|
||||
}, {
|
||||
[setAutoInstallRelease]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
AutoInstallRelease: action.payload,
|
||||
}
|
||||
},
|
||||
[setDismissDependencies]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import * as Constants from '../../constants';
|
||||
import {createReducer} from '@reduxjs/toolkit';
|
||||
|
||||
import * as Constants from '../../constants';
|
||||
import {
|
||||
addRemoteMount2,
|
||||
addS3Mount2,
|
||||
DISPLAY_CONFIGURATION,
|
||||
removeRemoteMount3,
|
||||
removeMount3,
|
||||
RESET_MOUNTS_STATE,
|
||||
SET_ALLOW_MOUNT,
|
||||
SET_AUTO_MOUNT_PROCESSED,
|
||||
setBusy,
|
||||
SET_MOUNT_STATE,
|
||||
SET_MOUNTED,
|
||||
SET_PROVIDER_STATE
|
||||
SET_PROVIDER_STATE,
|
||||
setBusy
|
||||
} from '../actions/mount_actions';
|
||||
|
||||
export const createMountReducer = state => {
|
||||
let providerList = [
|
||||
...Constants.PROVIDER_LIST,
|
||||
...(state.RemoteMounts||[]),
|
||||
...(state.RemoteMounts || []),
|
||||
...(state.S3Mounts || []),
|
||||
];
|
||||
const providerState = providerList.map(provider=> {
|
||||
const providerState = providerList
|
||||
.map(provider => {
|
||||
return {
|
||||
[provider]: {
|
||||
AutoMount: false,
|
||||
@@ -26,14 +30,13 @@ export const createMountReducer = state => {
|
||||
MountLocation: '',
|
||||
}
|
||||
}
|
||||
}).reduce((map, obj) => {
|
||||
return {
|
||||
...map,
|
||||
...obj
|
||||
}
|
||||
})
|
||||
.reduce((map, obj) => {
|
||||
return {...map, ...obj}
|
||||
});
|
||||
|
||||
const mountState = providerList.map(provider => {
|
||||
const mountState = providerList
|
||||
.map(provider => {
|
||||
return {
|
||||
[provider]: {
|
||||
AllowMount: false,
|
||||
@@ -41,154 +44,175 @@ export const createMountReducer = state => {
|
||||
Mounted: false,
|
||||
}
|
||||
}
|
||||
}).reduce((map, obj) => {
|
||||
return {
|
||||
...map,
|
||||
...obj
|
||||
}
|
||||
})
|
||||
.reduce((map, obj) => {
|
||||
return {...map, ...obj}
|
||||
});
|
||||
|
||||
const autoMountProcessed = providerList.map(provider => {
|
||||
return {
|
||||
[provider]: false,
|
||||
}
|
||||
}).reduce((map, obj) => {
|
||||
return {
|
||||
...map,
|
||||
...obj
|
||||
}
|
||||
});
|
||||
const autoMountProcessed =
|
||||
providerList.map(provider => {
|
||||
return {[provider]: false,}
|
||||
})
|
||||
.reduce((map, obj) => {
|
||||
return {...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],
|
||||
}
|
||||
return createReducer(
|
||||
{
|
||||
AutoMountProcessed: autoMountProcessed,
|
||||
DisplayConfiguration: null,
|
||||
DisplayRemoteConfiguration: false,
|
||||
DisplayS3Configuration: false,
|
||||
MountsBusy: false,
|
||||
MountState: mountState,
|
||||
ProviderState: providerState,
|
||||
RemoteMounts: state.RemoteMounts ? state.RemoteMounts : [],
|
||||
S3Mounts: state.S3Mounts ? state.S3Mounts : [],
|
||||
},
|
||||
[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];
|
||||
{
|
||||
[addRemoteMount2]: (state, action) => {
|
||||
let mountState = {...state.MountState};
|
||||
mountState[action.payload] = {
|
||||
AllowMount: false,
|
||||
DriveLetters: [],
|
||||
Mounted: false,
|
||||
};
|
||||
|
||||
let providerState = {...state.ProviderState};
|
||||
delete providerState[action.payload];
|
||||
let providerState = {...state.ProviderState};
|
||||
providerState[action.payload] = {
|
||||
AutoMount: false,
|
||||
AutoRestart: false,
|
||||
MountLocation: '',
|
||||
};
|
||||
|
||||
let autoMountProcessed = {...state.AutoMountProcessed};
|
||||
delete autoMountProcessed[action.payload];
|
||||
let autoMountProcessed = {...state.AutoMountProcessed};
|
||||
autoMountProcessed[action.payload] = true;
|
||||
|
||||
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,
|
||||
return {
|
||||
...state, AutoMountProcessed: autoMountProcessed,
|
||||
MountState: mountState, ProviderState: providerState,
|
||||
RemoteMounts: [...state.RemoteMounts, action.payload],
|
||||
}
|
||||
};
|
||||
},
|
||||
[SET_ALLOW_MOUNT]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
MountState: {
|
||||
...state.MountState,
|
||||
[action.payload.provider]: {
|
||||
...state.MountState[action.payload.provider],
|
||||
AllowMount: action.payload.allow,
|
||||
},
|
||||
[addS3Mount2]: (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,
|
||||
S3Mounts: [...state.S3Mounts, action.payload],
|
||||
}
|
||||
},
|
||||
[DISPLAY_CONFIGURATION]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
DisplayConfiguration: action.payload.provider,
|
||||
DisplayRemoteConfiguration: action.payload.remote,
|
||||
DisplayS3Configuration: action.payload.s3,
|
||||
};
|
||||
},
|
||||
[removeMount3]: (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);
|
||||
const s3Mounts =
|
||||
state.S3Mounts.filter(i => i !== action.payload);
|
||||
return {
|
||||
...state,
|
||||
AutoMountProcessed: autoMountProcessed,
|
||||
MountState: mountState,
|
||||
ProviderState: providerState,
|
||||
RemoteMounts: remoteMounts,
|
||||
S3Mounts: s3Mounts,
|
||||
};
|
||||
},
|
||||
[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
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
13
src/redux/reducers/pinned_manager_reducer.js
Normal file
13
src/redux/reducers/pinned_manager_reducer.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import {createReducer} from '@reduxjs/toolkit';
|
||||
import {displayPinnedManager} from '../actions/pinned_manager_actions';
|
||||
|
||||
export const pinnedManagerReducer = createReducer({
|
||||
DisplayPinnedManager: false,
|
||||
}, {
|
||||
[displayPinnedManager]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
DisplayPinnedManager: action.payload,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -15,10 +15,12 @@ const versionLookup = Constants.RELEASE_TYPES.map(k=> {
|
||||
|
||||
export const releaseVersionReducer = createReducer({
|
||||
AllowDismissDependencies: false,
|
||||
DismissNewReleasesAvailable: true,
|
||||
InstalledVersion: 'none',
|
||||
LocationsLookup: {},
|
||||
Release: 0,
|
||||
ReleaseDefault: 0,
|
||||
NewReleasesAvailable: [],
|
||||
NewReleasesAvailable2: [],
|
||||
Release: Constants.DEFAULT_RELEASE,
|
||||
ReleaseUpgradeAvailable: false,
|
||||
UpgradeAvailable: false,
|
||||
UpgradeData: null,
|
||||
@@ -49,6 +51,12 @@ export const releaseVersionReducer = createReducer({
|
||||
AllowDismissDependencies: action.payload,
|
||||
};
|
||||
},
|
||||
[Actions.setDismissNewReleasesAvailable]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
DismissNewReleasesAvailable: action.payload,
|
||||
};
|
||||
},
|
||||
[Actions.setDismissUIUpgrade]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
@@ -61,6 +69,19 @@ export const releaseVersionReducer = createReducer({
|
||||
InstalledVersion: action.payload,
|
||||
}
|
||||
},
|
||||
[Actions.setNewReleasesAvailable]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
DismissNewReleasesAvailable: false,
|
||||
NewReleasesAvailable: action.payload,
|
||||
};
|
||||
},
|
||||
[Actions.setNewReleasesAvailable2]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
NewReleasesAvailable2: action.payload,
|
||||
};
|
||||
},
|
||||
[Actions.SET_RELEASE_DATA]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
|
||||
20
src/redux/reducers/skynet_reducer.js
Normal file
20
src/redux/reducers/skynet_reducer.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import {createReducer} from '@reduxjs/toolkit';
|
||||
import * as Actions from '../actions/skynet_actions';
|
||||
|
||||
export const skynetReducer = createReducer({
|
||||
DisplayExport: false,
|
||||
DisplayImport: false,
|
||||
}, {
|
||||
[Actions.displaySkynetExport]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
DisplayExport: action.payload,
|
||||
}
|
||||
},
|
||||
[Actions.displaySkynetImport]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
DisplayImport: action.payload,
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -5,6 +5,8 @@ import {errorReducer} from '../reducers/error_reducer';
|
||||
import {installReducer} from '../reducers/install_reducer';
|
||||
import {createMountReducer} from '../reducers/mount_reducer';
|
||||
import {releaseVersionReducer} from '../reducers/release_version_reducer';
|
||||
import {skynetReducer} from '../reducers/skynet_reducer';
|
||||
import {pinnedManagerReducer} from '../reducers/pinned_manager_reducer'
|
||||
|
||||
export default function createAppStore(platformInfo, version, state) {
|
||||
const reducer = {
|
||||
@@ -14,6 +16,8 @@ export default function createAppStore(platformInfo, version, state) {
|
||||
install: installReducer,
|
||||
mounts: createMountReducer(state),
|
||||
relver: releaseVersionReducer,
|
||||
skynet: skynetReducer,
|
||||
pinned: pinnedManagerReducer,
|
||||
};
|
||||
|
||||
const middleware = [...getDefaultMiddleware()];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const Constants = require('../../constants');
|
||||
|
||||
const addListeners = (ipcMain, closeApplication, setWindowVisibility) => {
|
||||
const addListeners = (ipcMain, {closeApplication, setWindowVisibility}) => {
|
||||
ipcMain.on(Constants.IPC_Shutdown, () => {
|
||||
closeApplication();
|
||||
});
|
||||
@@ -17,4 +17,4 @@ const addListeners = (ipcMain, closeApplication, setWindowVisibility) => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const Constants = require('../../constants');
|
||||
const helpers = require('../../helpers');
|
||||
|
||||
const addListeners = (ipcMain, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
|
||||
helpers
|
||||
.getConfig(data.Version, data.Provider, data.Remote)
|
||||
.getConfig(data.Version, data.Provider, data.Remote, data.S3)
|
||||
.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, data.Remote)
|
||||
.getConfigTemplate(data.Version, data.Provider, data.Remote, data.S3)
|
||||
.then((data) => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {
|
||||
Template: data,
|
||||
@@ -36,12 +36,12 @@ 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.Remote, data.Version)
|
||||
.setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Remote, data.S3, data.Version)
|
||||
.then(() => {
|
||||
setConfigValue(++i);
|
||||
})
|
||||
.catch(() => {
|
||||
setConfigValue(++i);
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}, error);
|
||||
});
|
||||
} else {
|
||||
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {});
|
||||
@@ -53,4 +53,4 @@ const addListeners = (ipcMain, standardIPCReply) => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Constants = require('../../constants');
|
||||
const helpers = require('../../helpers');
|
||||
|
||||
const addListeners = (ipcMain, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => {
|
||||
helpers
|
||||
.checkDaemonVersion(data.Version, data.Provider)
|
||||
@@ -44,4 +44,4 @@ const addListeners = (ipcMain, standardIPCReply) => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ const Constants = require('../../constants');
|
||||
const fs = require('fs');
|
||||
const helpers = require('../../helpers');
|
||||
|
||||
const addListeners = (ipcMain, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Check_Dependency_Installed, (event, data) => {
|
||||
try {
|
||||
const exists = fs.lstatSync(data.File).isFile();
|
||||
@@ -72,15 +72,7 @@ const addListeners = (ipcMain, standardIPCReply) => {
|
||||
};
|
||||
if (data.IsWinFSP) {
|
||||
helpers
|
||||
.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"
|
||||
])
|
||||
.performWindowsUninstall(Constants.WINFSP_VERSION_NAMES)
|
||||
.then(uninstalled => {
|
||||
if (uninstalled) {
|
||||
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
|
||||
|
||||
@@ -2,7 +2,7 @@ const Constants = require('../../constants');
|
||||
const helpers = require('../../helpers');
|
||||
const path = require('path');
|
||||
|
||||
const addListeners = (ipcMain, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Download_File, (event, data) => {
|
||||
const destination = path.join(helpers.getDataDirectory(), data.Filename);
|
||||
helpers.downloadFile(data.URL, destination, (progress) => {
|
||||
@@ -22,4 +22,4 @@ const addListeners = (ipcMain, standardIPCReply) => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Constants = require('../../constants');
|
||||
const fs = require('fs');
|
||||
|
||||
const addListeners = (ipcMain, getMainWindow, dialog) => {
|
||||
const addListeners = (ipcMain, {getMainWindow, dialog}) => {
|
||||
ipcMain.on(Constants.IPC_Browse_Directory + '_sync', (event, data) => {
|
||||
dialog.showOpenDialog(getMainWindow(), {
|
||||
defaultPath: data.Location,
|
||||
@@ -28,4 +28,4 @@ const addListeners = (ipcMain, getMainWindow, dialog) => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,29 +20,29 @@ const clearManualMountDetection = provider => {
|
||||
const monitorMount = (sender, provider, providerList, version, pid, location) => {
|
||||
manualMountDetection[provider] = setInterval(() => {
|
||||
helpers
|
||||
.detectRepertoryMounts(version, providerList)
|
||||
.then(result => {
|
||||
if (result[provider].PID !== pid) {
|
||||
if (result[provider].PID === -1) {
|
||||
clearManualMountDetection(provider);
|
||||
sender.send(Constants.IPC_Unmount_Drive_Reply, {
|
||||
data: {
|
||||
Expected: expectedUnmount[provider],
|
||||
Location: location,
|
||||
Provider: provider,
|
||||
Error: Error(provider + ' Unmounted').toString(),
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
} else {
|
||||
pid = result[provider].PID;
|
||||
}
|
||||
.detectRepertoryMounts(version, providerList)
|
||||
.then(result => {
|
||||
if (result[provider].PID !== pid) {
|
||||
if (result[provider].PID === -1) {
|
||||
clearManualMountDetection(provider);
|
||||
sender.send(Constants.IPC_Unmount_Drive_Reply, {
|
||||
data: {
|
||||
Expected: expectedUnmount[provider],
|
||||
Location: location,
|
||||
Provider: provider,
|
||||
Error: Error(provider + ' Unmounted').toString(),
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
} else {
|
||||
pid = result[provider].PID;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
},6000);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}, 6000);
|
||||
};
|
||||
|
||||
const unmountAllDrives = () => {
|
||||
@@ -53,16 +53,16 @@ const unmountAllDrives = () => {
|
||||
}
|
||||
|
||||
// Unmount all items
|
||||
for (const i in mountedLocations) {
|
||||
const data = mountedData[mountedLocations[i]];
|
||||
helpers.stopMountProcessSync(data.Version, data.Provider, data.Remote);
|
||||
for (const mountLocation of mountedLocations) {
|
||||
const data = mountedData[mountLocation];
|
||||
helpers.stopMountProcessSync(data.Version, data.Provider, data.Remote, data.S3);
|
||||
}
|
||||
|
||||
mountedLocations = [];
|
||||
mountedData = {};
|
||||
};
|
||||
|
||||
const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Check_Mount_Location + '_sync', (event, data) => {
|
||||
let response = {
|
||||
Success: true,
|
||||
@@ -93,6 +93,7 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
const providerList = [
|
||||
...Constants.PROVIDER_LIST,
|
||||
...data.RemoteMounts,
|
||||
...data.S3Mounts,
|
||||
];
|
||||
for (const provider of providerList) {
|
||||
driveLetters[provider] = [];
|
||||
@@ -134,11 +135,11 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
}
|
||||
};
|
||||
|
||||
const setImage = (locations) => {
|
||||
const setImage = locations => {
|
||||
let driveInUse;
|
||||
if (Object.keys(locations).length > 0) {
|
||||
for (const provider of providerList) {
|
||||
driveInUse = locations[provider].length > 0;
|
||||
driveInUse = locations[provider] && locations[provider].length > 0;
|
||||
if (driveInUse)
|
||||
break;
|
||||
}
|
||||
@@ -148,52 +149,52 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
};
|
||||
|
||||
helpers
|
||||
.detectRepertoryMounts(data.Version, providerList)
|
||||
.then((results) => {
|
||||
let storageData = {};
|
||||
let locations = {};
|
||||
for (const provider of providerList) {
|
||||
storageData[provider] = results[provider] ? results[provider] : {
|
||||
Active: false,
|
||||
Location: '',
|
||||
PID: -1,
|
||||
};
|
||||
locations[provider] = storageData[provider].Location;
|
||||
.detectRepertoryMounts(data.Version, providerList)
|
||||
.then((results) => {
|
||||
let storageData = {};
|
||||
let locations = {};
|
||||
for (const provider of providerList) {
|
||||
storageData[provider] = results[provider] ? results[provider] : {
|
||||
Active: false,
|
||||
Location: '',
|
||||
PID: -1,
|
||||
};
|
||||
locations[provider] = storageData[provider].Location;
|
||||
|
||||
if (storageData[provider].PID !== -1) {
|
||||
expectedUnmount[provider] = false;
|
||||
if (firstMountCheck) {
|
||||
monitorMount(event.sender, provider, providerList, data.Version, storageData[provider].PID, storageData[provider].Location);
|
||||
}
|
||||
if (storageData[provider].PID !== -1) {
|
||||
expectedUnmount[provider] = false;
|
||||
if (firstMountCheck) {
|
||||
monitorMount(event.sender, provider, providerList, data.Version, storageData[provider].PID, storageData[provider].Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (os.platform() === 'win32') {
|
||||
grabDriveLetters(locations);
|
||||
}
|
||||
if (os.platform() === 'win32') {
|
||||
grabDriveLetters(locations);
|
||||
}
|
||||
|
||||
setImage(locations);
|
||||
if (firstMountCheck) {
|
||||
firstMountCheck = false;
|
||||
}
|
||||
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
|
||||
Active: storageData[provider].Active,
|
||||
DriveLetters: driveLetters[provider],
|
||||
Location: locations[provider],
|
||||
PID: storageData[provider].PID,
|
||||
Provider: provider,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
if (os.platform() === 'win32') {
|
||||
grabDriveLetters({});
|
||||
}
|
||||
setImage({});
|
||||
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
|
||||
DriveLetters: driveLetters[provider],
|
||||
Provider: provider,
|
||||
}, error);
|
||||
setImage(locations);
|
||||
if (firstMountCheck) {
|
||||
firstMountCheck = false;
|
||||
}
|
||||
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
|
||||
Active: storageData[provider].Active,
|
||||
DriveLetters: driveLetters[provider],
|
||||
Location: locations[provider],
|
||||
PID: storageData[provider].PID,
|
||||
Provider: provider,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
if (os.platform() === 'win32') {
|
||||
grabDriveLetters({});
|
||||
}
|
||||
setImage({});
|
||||
standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
|
||||
DriveLetters: driveLetters[provider],
|
||||
Provider: provider,
|
||||
}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
|
||||
@@ -206,6 +207,7 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
mountedData[data.Location] = {
|
||||
Provider: data.Provider,
|
||||
Remote: data.Remote,
|
||||
S3: data.S3,
|
||||
Version: data.Version,
|
||||
};
|
||||
const errorHandler = (pid, error) => {
|
||||
@@ -219,16 +221,18 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
Location: data.Location,
|
||||
Provider: data.Provider,
|
||||
Remote: data.Remote,
|
||||
S3: data.S3,
|
||||
}, error || Error(data.Provider + ' Unmounted'));
|
||||
};
|
||||
helpers
|
||||
.executeMount(data.Version, data.Provider, data.Remote, data.Location, (error, pid) => {
|
||||
.executeMount(data.Version, data.Provider, data.Remote, data.S3, data.Location, (error, pid) => {
|
||||
errorHandler(pid, error);
|
||||
})
|
||||
.then(() => {
|
||||
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
|
||||
Provider: data.Provider,
|
||||
Remote: data.Remote,
|
||||
S3: data.S3,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -237,19 +241,21 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Remove_Remote_Mount, (event, data) => {
|
||||
data = data.replace(':', '_');
|
||||
const dataDirectory = path.resolve(path.join(helpers.getDataDirectory(), '..', 'remote', data));
|
||||
ipcMain.on(Constants.IPC_Remove_Mount, (event, data) => {
|
||||
if (data.Remote) {
|
||||
data.Name = data.Name.replace(':', '_');
|
||||
}
|
||||
const dataDirectory = path.resolve(path.join(helpers.getDataDirectory(), '..', data.Remote ? 'remote' : 's3', data.Name));
|
||||
|
||||
try {
|
||||
helpers.removeDirectoryRecursively(dataDirectory);
|
||||
standardIPCReply(event, Constants.IPC_Remove_Remote_Mount_Reply, {DataDirectory: dataDirectory});
|
||||
standardIPCReply(event, Constants.IPC_Remove_Mount_Reply, {DataDirectory: dataDirectory});
|
||||
} catch (e) {
|
||||
standardIPCReply(event, Constants.IPC_Remove_Remote_Mount_Reply, {DataDirectory: dataDirectory}, e);
|
||||
standardIPCReply(event, Constants.IPC_Remove_Mount_Reply, {DataDirectory: dataDirectory}, e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Unmount_All_Drives, (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Unmount_All_Drives, event => {
|
||||
unmountAllDrives();
|
||||
standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply);
|
||||
});
|
||||
@@ -259,13 +265,13 @@ const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
|
||||
|
||||
expectedUnmount[data.Provider] = true;
|
||||
helpers
|
||||
.stopMountProcess(data.Version, data.Provider, data.Remote)
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
.stopMountProcess(data.Version, data.Provider, data.Remote, data.S3)
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
49
src/renderer/ipc/PinnedIPC.js
Normal file
49
src/renderer/ipc/PinnedIPC.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const Constants = require('../../constants');
|
||||
const helpers = require('../../helpers');
|
||||
|
||||
const addListeners = (ipcMain, {standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Get_Directory_Items, (event, data) => {
|
||||
helpers
|
||||
.grabDirectoryItems(data.Path, data.Version, data.Provider, data.Remote, data.S3)
|
||||
.then(data => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
|
||||
Items: data.items,
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Get_Pinned_Files, (event, data) => {
|
||||
helpers
|
||||
.grabDirectoryItems(data.Path, data.Version, data.Provider, data.Remote, data.S3)
|
||||
.then(data => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {
|
||||
Items: data.items,
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Directory_Items_Reply, {}, e);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Get_Pinned_Files_Status, (event, data) => {
|
||||
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Set_Pinned + '_sync', (event, data) => {
|
||||
helpers
|
||||
.setPinned(data.Path, data.Pinned, data.Version, data.Provider, data.Remote, data.S3)
|
||||
.then(success => {
|
||||
event.returnValue = success;
|
||||
})
|
||||
.catch(e => {
|
||||
event.returnValue = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
@@ -14,7 +14,7 @@ const setPlatformOverride = platformOverride => {
|
||||
_platformOverride = platformOverride;
|
||||
};
|
||||
|
||||
const addListeners = (ipcMain, detectScript, saveUiSettings) => {
|
||||
const addListeners = (ipcMain, {detectScript, saveUiSettings}) => {
|
||||
ipcMain.on(Constants.IPC_Get_Platform, (event) => {
|
||||
const sendResponse = (appPlatform, platform) => {
|
||||
event.sender.send(Constants.IPC_Get_Platform_Reply, {
|
||||
@@ -76,4 +76,4 @@ module.exports = {
|
||||
getPlatformOverride,
|
||||
setPlatformOverride,
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
const unzip = require('unzipper');
|
||||
|
||||
const addListeners = (ipcMain, getCleanupReleases, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {getCleanupReleases, standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Check_Installed, (event, data) => {
|
||||
const destination = path.join(helpers.getDataDirectory(), data.Version);
|
||||
helpers
|
||||
|
||||
47
src/renderer/ipc/SkynetIPC.js
Normal file
47
src/renderer/ipc/SkynetIPC.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const Constants = require('../../constants');
|
||||
const helpers = require('../../helpers');
|
||||
|
||||
const addListeners = (ipcMain, {standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Export_Skylinks, (event, data) => {
|
||||
helpers
|
||||
.exportSkylinks(data.Version, data.Paths)
|
||||
.then(result => {
|
||||
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {
|
||||
Result: result,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Export_Skylinks_Reply, {}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Grab_Skynet_Tree, (event, data) => {
|
||||
helpers
|
||||
.grabSkynetFileTree(data.Version)
|
||||
.then(result => {
|
||||
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {
|
||||
Result: result,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Grab_Skynet_Tree_Reply, {}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Import_Skylinks, (event, data) => {
|
||||
helpers
|
||||
.importSkylinks(data.Version, data.JsonArray)
|
||||
.then(result => {
|
||||
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {
|
||||
Result: result,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Import_Skylinks_Reply, {}, error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
@@ -3,19 +3,70 @@ const fs = require('fs');
|
||||
const helpers = require('../../helpers');
|
||||
const path = require('path');
|
||||
|
||||
const getDirectories = source => {
|
||||
try {
|
||||
return fs.readdirSync(source, {withFileTypes: true})
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const addListeners = ipcMain => {
|
||||
ipcMain.on(Constants.IPC_Get_State, event => {
|
||||
helpers.mkDirByPathSync(helpers.getDataDirectory());
|
||||
|
||||
let data = {};
|
||||
const configFile = path.join(helpers.getDataDirectory(), 'settings.json');
|
||||
if (fs.existsSync(configFile)) {
|
||||
event.sender.send(Constants.IPC_Get_State_Reply, {
|
||||
data: JSON.parse(fs.readFileSync(configFile, 'utf8')),
|
||||
});
|
||||
data = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
||||
} else {
|
||||
event.sender.send(Constants.IPC_Get_State_Reply, {
|
||||
data: null,
|
||||
});
|
||||
data.Release = Constants.DEFAULT_RELEASE;
|
||||
data.Version = -1;
|
||||
}
|
||||
|
||||
for (const provider of Constants.PROVIDER_LIST) {
|
||||
if (!data[provider]) {
|
||||
data[provider] = {
|
||||
AutoMount: false,
|
||||
AutoRestart: true,
|
||||
MountLocation: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.RemoteMounts = data.RemoteMounts || [];
|
||||
data.S3Mounts = data.S3Mounts || [];
|
||||
|
||||
const remoteItems = getDirectories(path.join(helpers.getRepertoryDirectory(), 'remote'));
|
||||
for (const dir of remoteItems) {
|
||||
const name = 'Remote' + dir.replace('_', ':');
|
||||
if (!data.RemoteMounts || data.RemoteMounts.indexOf(name) === -1) {
|
||||
data.RemoteMounts.push(name);
|
||||
data[name] = {
|
||||
AutoMount: false,
|
||||
AutoRestart: true,
|
||||
MountLocation: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const s3Items = getDirectories(path.join(helpers.getRepertoryDirectory(), 's3'));
|
||||
for (const dir of s3Items) {
|
||||
const name = 'S3' + dir;
|
||||
if (!data.S3Mounts || data.S3Mounts.indexOf(name) === -1) {
|
||||
data.S3Mounts.push(name);
|
||||
data[name] = {
|
||||
AutoMount: false,
|
||||
AutoRestart: true,
|
||||
MountLocation: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
event.sender.send(Constants.IPC_Get_State_Reply, {
|
||||
data,
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Save_State, (event, data) => {
|
||||
@@ -27,4 +78,4 @@ const addListeners = ipcMain => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ const Constants = require('../../constants');
|
||||
const os = require('os');
|
||||
const helpers = require('../../helpers');
|
||||
|
||||
const addListeners = (ipcMain, closeApplication) => {
|
||||
const addListeners = (ipcMain, {closeApplication}) => {
|
||||
ipcMain.on(Constants.IPC_Reboot_System, () => {
|
||||
if (os.platform() === 'win32') {
|
||||
helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']);
|
||||
@@ -13,4 +13,4 @@ const addListeners = (ipcMain, closeApplication) => {
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs');
|
||||
const helpers = require('../../helpers');
|
||||
const os = require('os');
|
||||
|
||||
const addListeners = (ipcMain, setIsInstalling, unmountAllDrives, standardIPCReply) => {
|
||||
const addListeners = (ipcMain, {setIsInstalling, unmountAllDrives, standardIPCReply}) => {
|
||||
ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => {
|
||||
let allowSkipVerification = true;
|
||||
|
||||
@@ -113,4 +113,4 @@ const addListeners = (ipcMain, setIsInstalling, unmountAllDrives, standardIPCRep
|
||||
|
||||
module.exports = {
|
||||
addListeners
|
||||
};
|
||||
};
|
||||
|
||||
82
src/utils.js
82
src/utils.js
@@ -1,13 +1,29 @@
|
||||
import React from 'react';
|
||||
import * as Constants from './constants';
|
||||
import Modal from './components/UI/Modal/Modal';
|
||||
import axios from 'axios';
|
||||
|
||||
const ipcRenderer = (!process.versions.hasOwnProperty('electron') && window && window.require) ?
|
||||
window.require('electron').ipcRenderer :
|
||||
null;
|
||||
|
||||
export const createModalConditionally = (condition, jsx, critical) => {
|
||||
const modalProps = {critical: critical};
|
||||
export const checkNewReleases = selectedVersion => {
|
||||
let previousReleases = localStorage.getItem('previous_releases');
|
||||
if (previousReleases) {
|
||||
previousReleases = JSON.parse(previousReleases).VersionLookup;
|
||||
|
||||
let currentReleases = localStorage.getItem('releases');
|
||||
if (currentReleases) {
|
||||
currentReleases = JSON.parse(currentReleases).VersionLookup;
|
||||
return getNewReleases(previousReleases, currentReleases, selectedVersion);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
export const createModalConditionally = (condition, jsx, critical, disableFocusTrap, transparent) => {
|
||||
const modalProps = {critical: critical, disableFocusTrap: disableFocusTrap, transparent: transparent};
|
||||
return condition ? (<Modal {...modalProps}>{jsx}</Modal>) : null;
|
||||
};
|
||||
|
||||
@@ -16,10 +32,72 @@ export const extractFileNameFromURL = url => {
|
||||
return parts[parts.length - 1];
|
||||
};
|
||||
|
||||
export const formatLinesForDisplay = lines => {
|
||||
let msg = '';
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (i > 1) {
|
||||
msg += '\n';
|
||||
}
|
||||
msg += (lines[i].replace(/(\\#)/gm, '#') + '\n');
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
export const getChangesForRepertoryVersion = version => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = 'https://bitbucket.org/blockstorage/repertory/raw/' +
|
||||
Constants.REPERTORY_BRANCH + '/CHANGELOG.md';
|
||||
axios
|
||||
.get(url, {
|
||||
responseType: 'text',
|
||||
})
|
||||
.then(response => {
|
||||
try {
|
||||
let found = false;
|
||||
let ended = false;
|
||||
let lines = response.data
|
||||
.replace(/(\r\n)/gm, '\n')
|
||||
.split('\n')
|
||||
.filter(l => {
|
||||
return !ended && (l.length > 0) && (found
|
||||
? !(ended = l.startsWith('## '))
|
||||
: (found = l.startsWith(`## ${version}`)));
|
||||
});
|
||||
resolve(lines);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getIPCRenderer = () => {
|
||||
return ipcRenderer;
|
||||
};
|
||||
|
||||
export const getNewReleases = (existingLocations, newLocations, selectedVersion) => {
|
||||
const ret = [];
|
||||
if (existingLocations && newLocations) {
|
||||
Constants.RELEASE_TYPES.forEach(release => {
|
||||
newLocations[release]
|
||||
.filter(version => (version !== selectedVersion) && !existingLocations[release].includes(version) && (version !== 'unavailable'))
|
||||
.forEach(version => {
|
||||
ret.splice(0, 0, {
|
||||
Display: version,
|
||||
Release: Constants.RELEASE_TYPES.indexOf(release),
|
||||
Version: newLocations[release].indexOf(version),
|
||||
VersionString: version,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const getSelectedVersionFromState = state => {
|
||||
return (state.relver.Version === -1) ?
|
||||
'unavailable' :
|
||||
|
||||
Reference in New Issue
Block a user