summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAAGaming <aa@mail.catvibers.me>2022-07-14 22:51:55 -0400
committerGitHub <noreply@github.com>2022-07-14 22:51:55 -0400
commit8c8cf180fad2ad6951ad7ce6b74e6c163fa01d18 (patch)
treedd051aaa7928867657c955cf2cad5143ca72f658
parent05d11cfff037734f7aa6b6ac8e701eacd9f38d60 (diff)
downloaddecky-loader-8c8cf180fad2ad6951ad7ce6b74e6c163fa01d18.tar.gz
decky-loader-8c8cf180fad2ad6951ad7ce6b74e6c163fa01d18.zip
Updater for decky-loader (#117)
* Add an updater in settings for decky-loader * add chmod * remove junk comments
-rw-r--r--backend/main.py2
-rw-r--r--backend/updater.py113
-rwxr-xr-xdist/install_prerelease.sh12
-rw-r--r--frontend/package.json4
-rw-r--r--frontend/pnpm-lock.yaml200
-rw-r--r--frontend/src/components/settings/index.tsx4
-rw-r--r--frontend/src/components/settings/pages/general/Updater.tsx86
-rw-r--r--frontend/src/components/settings/pages/general/index.tsx (renamed from frontend/src/components/settings/pages/GeneralSettings.tsx)8
-rw-r--r--frontend/src/components/settings/pages/plugin_list/index.tsx (renamed from frontend/src/components/settings/pages/PluginList.tsx)2
-rw-r--r--frontend/src/index.tsx2
-rw-r--r--frontend/src/updater.ts30
-rw-r--r--frontend/tsconfig.json1
12 files changed, 353 insertions, 111 deletions
diff --git a/backend/main.py b/backend/main.py
index 99e6c618..52e0ce06 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -27,6 +27,7 @@ from browser import PluginBrowser
from injector import inject_to_tab, tab_has_global_var
from loader import Loader
from utilities import Utilities
+from updater import Updater
logger = getLogger("Main")
@@ -47,6 +48,7 @@ class PluginManager:
self.plugin_loader = Loader(self.web_app, CONFIG["plugin_path"], self.loop, CONFIG["live_reload"])
self.plugin_browser = PluginBrowser(CONFIG["plugin_path"], self.web_app)
self.utilities = Utilities(self)
+ self.updater = Updater(self)
jinja_setup(self.web_app)
self.web_app.on_startup.append(self.inject_javascript)
diff --git a/backend/updater.py b/backend/updater.py
new file mode 100644
index 00000000..d7aed0d7
--- /dev/null
+++ b/backend/updater.py
@@ -0,0 +1,113 @@
+import uuid
+from logging import getLogger
+from json.decoder import JSONDecodeError
+
+from asyncio import sleep
+
+from aiohttp import ClientSession, web
+
+from injector import inject_to_tab, get_tab
+
+from os import getcwd, path
+
+from subprocess import call
+
+import helpers
+
+logger = getLogger("Updater")
+
+class Updater:
+ def __init__(self, context) -> None:
+ self.context = context
+ self.updater_methods = {
+ "get_version": self.get_version,
+ "do_update": self.do_update,
+ "do_restart": self.do_restart
+ }
+ self.remoteVer = None
+ try:
+ with open(path.join(getcwd(), ".loader.version"), 'r') as version_file:
+ self.localVer = version_file.readline().replace("\n", "")
+ except:
+ self.localVer = False
+
+ if context:
+ context.web_app.add_routes([
+ web.post("/updater/{method_name}", self._handle_server_method_call)
+ ])
+ context.loop.create_task(self.version_reloader())
+
+ async def _handle_server_method_call(self, request):
+ method_name = request.match_info["method_name"]
+ try:
+ args = await request.json()
+ except JSONDecodeError:
+ args = {}
+ res = {}
+ try:
+ r = await self.updater_methods[method_name](**args)
+ res["result"] = r
+ res["success"] = True
+ except Exception as e:
+ res["result"] = str(e)
+ res["success"] = False
+ return web.json_response(res)
+
+ async def get_version(self):
+ if self.localVer:
+ return {
+ "current": self.localVer,
+ "remote": self.remoteVer,
+ "updatable": self.remoteVer != None
+ }
+ else:
+ return {"current": "unknown", "updatable": False}
+
+ async def version_reloader(self):
+ while True:
+ try:
+ async with ClientSession() as web:
+ async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases", ssl=helpers.get_ssl_context()) as res:
+ remoteVersions = await res.json()
+ self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].endswith("-pre"), remoteVersions), None)
+ logger.info("Updated remote version information")
+ except:
+ pass
+ await sleep(60 * 60) # 1 hour
+
+ async def do_update(self):
+ version = self.remoteVer["tag_name"]
+ #TODO don't hardcode this
+ download_url = self.remoteVer["assets"][0]["browser_download_url"]
+
+ tab = await get_tab("SP")
+ await tab.open_websocket()
+ async with ClientSession() as web:
+ async with web.request("GET", download_url, ssl=helpers.get_ssl_context(), allow_redirects=True) as res:
+ total = int(res.headers.get('content-length', 0))
+
+ with open(path.join(getcwd(), "PluginLoader"), "wb") as out:
+ progress = 0
+ raw = 0
+ async for c in res.content.iter_chunked(512):
+ out.write(c)
+ raw += len(c)
+ new_progress = round((raw / total) * 100)
+ if progress != new_progress:
+ if new_progress - progress>= 2:
+ self.context.loop.create_task(tab.evaluate_js(f"window.DeckyUpdater.updateProgress({progress})", False, False))
+ progress = new_progress
+
+ with open(path.join(getcwd(), ".loader.version"), "w") as out:
+ out.write(version)
+
+ call(['chmod', '+x', path.join(getcwd(), "PluginLoader")])
+
+ logger.info("Updated loader installation.")
+ await tab.evaluate_js("window.DeckyUpdater.finish()", False, False)
+ await tab.client.close()
+
+ async def do_restart(self):
+ call(["systemctl", "daemon-reload"])
+ call(["systemctl", "restart", "plugin_loader"])
+ exit(0)
diff --git a/dist/install_prerelease.sh b/dist/install_prerelease.sh
index 276f3587..384383d2 100755
--- a/dist/install_prerelease.sh
+++ b/dist/install_prerelease.sh
@@ -12,10 +12,14 @@ sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins
# Download latest release and install it
-DOWNLOADURL="$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))" | jq -r ".assets[].browser_download_url")"
+RELEASES="$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases')"
+RELEASE="$($RELEASES | jq -r "first(.[] | select(.prerelease == "true"))")"
+VERSION="$($RELEASE | jq -r ".tag_name")"
+DOWNLOADURL="$($RELEASE | jq -r ".assets[].browser_download_url")"
# printf "DOWNLOADURL=$DOWNLOADURL\n"
curl -L $DOWNLOADURL --output ${HOMEBREW_FOLDER}/services/PluginLoader
chmod +x ${HOMEBREW_FOLDER}/services/PluginLoader
+echo $VERSION > ${HOMEBREW_FOLDER}/services/.loader.version
systemctl --user stop plugin_loader 2> /dev/null
systemctl --user disable plugin_loader 2> /dev/null
@@ -30,9 +34,9 @@ Description=SteamDeck Plugin Loader
Type=simple
User=root
Restart=always
-ExecStart=/home/deck/homebrew/services/PluginLoader
-WorkingDirectory=/home/deck/homebrew/services
-Environment=PLUGIN_PATH=/home/deck/homebrew/plugins
+ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
+WorkingDirectory=${HOMEBREW_FOLDER}/services
+Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
Environment=LOG_LEVEL=DEBUG
[Install]
WantedBy=multi-user.target
diff --git a/frontend/package.json b/frontend/package.json
index 07cdeddc..b16452e1 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -26,7 +26,7 @@
"prettier-plugin-import-sort": "^0.0.7",
"react": "16.14.0",
"react-dom": "16.14.0",
- "rollup": "^2.75.7",
+ "rollup": "^2.76.0",
"tslib": "^2.4.0",
"typescript": "^4.7.4"
},
@@ -37,7 +37,7 @@
}
},
"dependencies": {
- "decky-frontend-lib": "^1.0.2",
+ "decky-frontend-lib": "^1.2.1",
"react-icons": "^4.4.0"
}
}
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index 89d21963..21bddef3 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -9,7 +9,7 @@ specifiers:
'@types/react': 16.14.0
'@types/react-router': 5.1.18
'@types/webpack': ^5.28.0
- decky-frontend-lib: ^1.0.2
+ decky-frontend-lib: ^1.2.1
husky: ^8.0.1
import-sort-style-module: ^6.0.0
inquirer: ^8.2.4
@@ -18,20 +18,20 @@ specifiers:
react: 16.14.0
react-dom: 16.14.0
react-icons: ^4.4.0
- rollup: ^2.75.7
+ rollup: ^2.76.0
tslib: ^2.4.0
typescript: ^4.7.4
dependencies:
- decky-frontend-lib: 1.0.2
+ decky-frontend-lib: 1.2.1
react-icons: 4.4.0_react@16.14.0
devDependencies:
- '@rollup/plugin-commonjs': 21.1.0_rollup@2.75.7
- '@rollup/plugin-json': 4.1.0_rollup@2.75.7
- '@rollup/plugin-node-resolve': 13.3.0_rollup@2.75.7
- '@rollup/plugin-replace': 4.0.0_rollup@2.75.7
- '@rollup/plugin-typescript': 8.3.3_g4qkabtmybowem44p7ts7jnbqm
+ '@rollup/plugin-commonjs': 21.1.0_rollup@2.76.0
+ '@rollup/plugin-json': 4.1.0_rollup@2.76.0
+ '@rollup/plugin-node-resolve': 13.3.0_rollup@2.76.0
+ '@rollup/plugin-replace': 4.0.0_rollup@2.76.0
+ '@rollup/plugin-typescript': 8.3.3_mrkdcqv53wzt2ybukxlrvz47fu
'@types/react': 16.14.0
'@types/react-router': 5.1.18
'@types/webpack': 5.28.0
@@ -42,7 +42,7 @@ devDependencies:
prettier-plugin-import-sort: 0.0.7_prettier@2.7.1
react: 16.14.0
react-dom: 16.14.0_react@16.14.0
- rollup: 2.75.7
+ rollup: 2.76.0
tslib: 2.4.0
typescript: 4.7.4
@@ -63,8 +63,8 @@ packages:
'@babel/highlight': 7.18.6
dev: true
- /@babel/compat-data/7.18.6:
- resolution: {integrity: sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==}
+ /@babel/compat-data/7.18.8:
+ resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==}
engines: {node: '>=6.9.0'}
dev: true
@@ -76,12 +76,12 @@ packages:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.18.7
'@babel/helper-compilation-targets': 7.18.6_@babel+core@7.18.6
- '@babel/helper-module-transforms': 7.18.6
+ '@babel/helper-module-transforms': 7.18.8
'@babel/helpers': 7.18.6
- '@babel/parser': 7.18.6
+ '@babel/parser': 7.18.8
'@babel/template': 7.18.6
- '@babel/traverse': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/traverse': 7.18.8
+ '@babel/types': 7.18.8
convert-source-map: 1.8.0
debug: 4.3.4
gensync: 1.0.0-beta.2
@@ -95,7 +95,7 @@ packages:
resolution: {integrity: sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
'@jridgewell/gen-mapping': 0.3.2
jsesc: 2.5.2
dev: true
@@ -106,10 +106,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/compat-data': 7.18.6
+ '@babel/compat-data': 7.18.8
'@babel/core': 7.18.6
'@babel/helper-validator-option': 7.18.6
- browserslist: 4.21.1
+ browserslist: 4.21.2
semver: 6.3.0
dev: true
@@ -123,25 +123,25 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
dev: true
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
dev: true
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
dev: true
- /@babel/helper-module-transforms/7.18.6:
- resolution: {integrity: sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==}
+ /@babel/helper-module-transforms/7.18.8:
+ resolution: {integrity: sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-environment-visitor': 7.18.6
@@ -150,8 +150,8 @@ packages:
'@babel/helper-split-export-declaration': 7.18.6
'@babel/helper-validator-identifier': 7.18.6
'@babel/template': 7.18.6
- '@babel/traverse': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/traverse': 7.18.8
+ '@babel/types': 7.18.8
transitivePeerDependencies:
- supports-color
dev: true
@@ -160,14 +160,14 @@ packages:
resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
dev: true
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
dev: true
/@babel/helper-validator-identifier/7.18.6:
@@ -185,8 +185,8 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.18.6
- '@babel/traverse': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/traverse': 7.18.8
+ '@babel/types': 7.18.8
transitivePeerDependencies:
- supports-color
dev: true
@@ -200,12 +200,12 @@ packages:
js-tokens: 4.0.0
dev: true
- /@babel/parser/7.18.6:
- resolution: {integrity: sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==}
+ /@babel/parser/7.18.8:
+ resolution: {integrity: sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
- '@babel/types': 7.18.7
+ '@babel/types': 7.18.8
dev: true
/@babel/template/7.18.6:
@@ -213,12 +213,12 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
- '@babel/parser': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/parser': 7.18.8
+ '@babel/types': 7.18.8
dev: true
- /@babel/traverse/7.18.6:
- resolution: {integrity: sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==}
+ /@babel/traverse/7.18.8:
+ resolution: {integrity: sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
@@ -227,16 +227,16 @@ packages:
'@babel/helper-function-name': 7.18.6
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
- '@babel/parser': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/parser': 7.18.8
+ '@babel/types': 7.18.8
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/types/7.18.7:
- resolution: {integrity: sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==}
+ /@babel/types/7.18.8:
+ resolution: {integrity: sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.18.6
@@ -260,8 +260,8 @@ packages:
'@jridgewell/trace-mapping': 0.3.14
dev: true
- /@jridgewell/resolve-uri/3.0.8:
- resolution: {integrity: sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==}
+ /@jridgewell/resolve-uri/3.1.0:
+ resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: true
@@ -284,61 +284,61 @@ packages:
/@jridgewell/trace-mapping/0.3.14:
resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==}
dependencies:
- '@jridgewell/resolve-uri': 3.0.8
+ '@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
- /@rollup/plugin-commonjs/21.1.0_rollup@2.75.7:
+ /@rollup/plugin-commonjs/21.1.0_rollup@2.76.0:
resolution: {integrity: sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==}
engines: {node: '>= 8.0.0'}
peerDependencies:
rollup: ^2.38.3
dependencies:
- '@rollup/pluginutils': 3.1.0_rollup@2.75.7
+ '@rollup/pluginutils': 3.1.0_rollup@2.76.0
commondir: 1.0.1
estree-walker: 2.0.2
glob: 7.2.3
is-reference: 1.2.1
magic-string: 0.25.9
resolve: 1.22.1
- rollup: 2.75.7
+ rollup: 2.76.0
dev: true
- /@rollup/plugin-json/4.1.0_rollup@2.75.7:
+ /@rollup/plugin-json/4.1.0_rollup@2.76.0:
resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
peerDependencies:
rollup: ^1.20.0 || ^2.0.0
dependencies:
- '@rollup/pluginutils': 3.1.0_rollup@2.75.7
- rollup: 2.75.7
+ '@rollup/pluginutils': 3.1.0_rollup@2.76.0
+ rollup: 2.76.0
dev: true
- /@rollup/plugin-node-resolve/13.3.0_rollup@2.75.7:
+ /@rollup/plugin-node-resolve/13.3.0_rollup@2.76.0:
resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==}
engines: {node: '>= 10.0.0'}
peerDependencies:
rollup: ^2.42.0
dependencies:
- '@rollup/pluginutils': 3.1.0_rollup@2.75.7
+ '@rollup/pluginutils': 3.1.0_rollup@2.76.0
'@types/resolve': 1.17.1
deepmerge: 4.2.2
is-builtin-module: 3.1.0
is-module: 1.0.0
resolve: 1.22.1
- rollup: 2.75.7
+ rollup: 2.76.0
dev: true
- /@rollup/plugin-replace/4.0.0_rollup@2.75.7:
+ /@rollup/plugin-replace/4.0.0_rollup@2.76.0:
resolution: {integrity: sha512-+rumQFiaNac9y64OHtkHGmdjm7us9bo1PlbgQfdihQtuNxzjpaB064HbRnewUOggLQxVCCyINfStkgmBeQpv1g==}
peerDependencies:
rollup: ^1.20.0 || ^2.0.0
dependencies:
- '@rollup/pluginutils': 3.1.0_rollup@2.75.7
+ '@rollup/pluginutils': 3.1.0_rollup@2.76.0
magic-string: 0.25.9
- rollup: 2.75.7
+ rollup: 2.76.0
dev: true
- /@rollup/plugin-typescript/8.3.3_g4qkabtmybowem44p7ts7jnbqm:
+ /@rollup/plugin-typescript/8.3.3_mrkdcqv53wzt2ybukxlrvz47fu:
resolution: {integrity: sha512-55L9SyiYu3r/JtqdjhwcwaECXP7JeJ9h1Sg1VWRJKIutla2MdZQodTgcCNybXLMCnqpNLEhS2vGENww98L1npg==}
engines: {node: '>=8.0.0'}
peerDependencies:
@@ -349,14 +349,14 @@ packages:
tslib:
optional: true
dependencies:
- '@rollup/pluginutils': 3.1.0_rollup@2.75.7
+ '@rollup/pluginutils': 3.1.0_rollup@2.76.0
resolve: 1.22.1
- rollup: 2.75.7
+ rollup: 2.76.0
tslib: 2.4.0
typescript: 4.7.4
dev: true
- /@rollup/pluginutils/3.1.0_rollup@2.75.7:
+ /@rollup/pluginutils/3.1.0_rollup@2.76.0:
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
peerDependencies:
@@ -365,18 +365,18 @@ packages:
'@types/estree': 0.0.39
estree-walker: 1.0.1
picomatch: 2.3.1
- rollup: 2.75.7
+ rollup: 2.76.0
dev: true
/@types/eslint-scope/3.7.4:
resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
dependencies:
- '@types/eslint': 8.4.3
+ '@types/eslint': 8.4.5
'@types/estree': 0.0.51
dev: true
- /@types/eslint/8.4.3:
- resolution: {integrity: sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw==}
+ /@types/eslint/8.4.5:
+ resolution: {integrity: sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==}
dependencies:
'@types/estree': 0.0.51
'@types/json-schema': 7.0.11
@@ -390,8 +390,8 @@ packages:
resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
dev: true
- /@types/estree/0.0.52:
- resolution: {integrity: sha512-BZWrtCU0bMVAIliIV+HJO1f1PR41M7NKjfxrFJwwhKI1KwhwOxYw1SXg9ao+CIMt774nFuGiG6eU+udtbEI9oQ==}
+ /@types/estree/1.0.0:
+ resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
dev: true
/@types/history/4.7.11:
@@ -402,8 +402,8 @@ packages:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true
- /@types/node/18.0.0:
- resolution: {integrity: sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==}
+ /@types/node/18.0.4:
+ resolution: {integrity: sha512-M0+G6V0Y4YV8cqzHssZpaNCqvYwlCiulmm0PwpNLF55r/+cT8Ol42CHRU1SEaYFH2rTwiiE1aYg/2g2rrtGdPA==}
dev: true
/@types/prop-types/15.7.5:
@@ -427,13 +427,13 @@ packages:
/@types/resolve/1.17.1:
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
dependencies:
- '@types/node': 18.0.0
+ '@types/node': 18.0.4
dev: true
/@types/webpack/5.28.0:
resolution: {integrity: sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==}
dependencies:
- '@types/node': 18.0.0
+ '@types/node': 18.0.4
tapable: 2.2.1
webpack: 5.73.0
transitivePeerDependencies:
@@ -643,15 +643,15 @@ packages:
concat-map: 0.0.1
dev: true
- /browserslist/4.21.1:
- resolution: {integrity: sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==}
+ /browserslist/4.21.2:
+ resolution: {integrity: sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- caniuse-lite: 1.0.30001361
- electron-to-chromium: 1.4.174
- node-releases: 2.0.5
- update-browserslist-db: 1.0.4_browserslist@4.21.1
+ caniuse-lite: 1.0.30001366
+ electron-to-chromium: 1.4.189
+ node-releases: 2.0.6
+ update-browserslist-db: 1.0.4_browserslist@4.21.2
dev: true
/buffer-from/1.1.2:
@@ -689,8 +689,8 @@ packages:
engines: {node: '>=4'}
dev: true
- /caniuse-lite/1.0.30001361:
- resolution: {integrity: sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ==}
+ /caniuse-lite/1.0.30001366:
+ resolution: {integrity: sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA==}
dev: true
/chalk/2.4.2:
@@ -771,7 +771,7 @@ packages:
dev: true
/concat-map/0.0.1:
- resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
dev: true
/convert-source-map/1.8.0:
@@ -806,8 +806,8 @@ packages:
ms: 2.1.2
dev: true
- /decky-frontend-lib/1.0.2:
- resolution: {integrity: sha512-l2Fq1oKkZdi2W4Qq+EXWgwOynWgANLTSFHcHrbJwCimTJ75uPlvrtsulnh5PlIovydM6iC+NyjAbW/qsVXWeLg==}
+ /decky-frontend-lib/1.2.1:
+ resolution: {integrity: sha512-aJmjOSMwQN9LTquYaMhSqW+FhmKLRgLb75JkcGRWKuIe8rjDfwwbAB/ckJseIC8UMzPKhspvcznfxyp+c72B5Q==}
dev: false
/deepmerge/4.2.2:
@@ -826,8 +826,8 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
- /electron-to-chromium/1.4.174:
- resolution: {integrity: sha512-JER+w+9MV2MBVFOXxP036bLlNOnzbYAWrWU8sNUwoOO69T3w4564WhM5H5atd8VVS8U4vpi0i0kdoYzm1NPQgQ==}
+ /electron-to-chromium/1.4.189:
+ resolution: {integrity: sha512-dQ6Zn4ll2NofGtxPXaDfY2laIa6NyCQdqXYHdwH90GJQW0LpJJib0ZU/ERtbb0XkBEmUD2eJtagbOie3pdMiPg==}
dev: true
/emoji-regex/8.0.0:
@@ -1038,9 +1038,9 @@ packages:
resolution: {integrity: sha512-NyShTiNhTh4Vy7kJUVe6CuvOaQAzzfSIT72wtp3CzGjz8bHjNj59DCAjncuviicmDOgVAgmLuSh1WMcLYAMWGg==}
dependencies:
'@babel/core': 7.18.6
- '@babel/parser': 7.18.6
- '@babel/traverse': 7.18.6
- '@babel/types': 7.18.7
+ '@babel/parser': 7.18.8
+ '@babel/traverse': 7.18.8
+ '@babel/types': 7.18.8
find-line-column: 0.5.2
transitivePeerDependencies:
- supports-color
@@ -1099,7 +1099,7 @@ packages:
mute-stream: 0.0.8
ora: 5.4.1
run-async: 2.4.1
- rxjs: 7.5.5
+ rxjs: 7.5.6
string-width: 4.2.3
strip-ansi: 6.0.1
through: 2.3.8
@@ -1145,7 +1145,7 @@ packages:
/is-reference/1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
dependencies:
- '@types/estree': 0.0.52
+ '@types/estree': 1.0.0
dev: true
/is-unicode-supported/0.1.0:
@@ -1157,7 +1157,7 @@ packages:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'}
dependencies:
- '@types/node': 18.0.0
+ '@types/node': 18.0.4
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
@@ -1265,8 +1265,8 @@ packages:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: true
- /node-releases/2.0.5:
- resolution: {integrity: sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==}
+ /node-releases/2.0.6:
+ resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
dev: true
/object-assign/4.1.1:
@@ -1437,8 +1437,8 @@ packages:
signal-exit: 3.0.7
dev: true
- /rollup/2.75.7:
- resolution: {integrity: sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ==}
+ /rollup/2.76.0:
+ resolution: {integrity: sha512-9jwRIEY1jOzKLj3nsY/yot41r19ITdQrhs+q3ggNWhr9TQgduHqANvPpS32RNpzGklJu3G1AJfvlZLi/6wFgWA==}
engines: {node: '>=10.0.0'}
hasBin: true
optionalDependencies:
@@ -1450,8 +1450,8 @@ packages:
engines: {node: '>=0.12.0'}
dev: true
- /rxjs/7.5.5:
- resolution: {integrity: sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==}
+ /rxjs/7.5.6:
+ resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==}
dependencies:
tslib: 2.4.0
dev: true
@@ -1592,12 +1592,12 @@ packages:
jest-worker: 27.5.1
schema-utils: 3.1.1
serialize-javascript: 6.0.0
- terser: 5.14.1
+ terser: 5.14.2
webpack: 5.73.0
dev: true
- /terser/5.14.1:
- resolution: {integrity: sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==}
+ /terser/5.14.2:
+ resolution: {integrity: sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==}
engines: {node: '>=10'}
hasBin: true
dependencies:
@@ -1644,13 +1644,13 @@ packages:
hasBin: true
dev: true
- /update-browserslist-db/1.0.4_browserslist@4.21.1:
+ /update-browserslist-db/1.0.4_browserslist@4.21.2:
resolution: {integrity: sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
- browserslist: 4.21.1
+ browserslist: 4.21.2
escalade: 3.1.1
picocolors: 1.0.0
dev: true
@@ -1701,7 +1701,7 @@ packages:
'@webassemblyjs/wasm-parser': 1.11.1
acorn: 8.7.1
acorn-import-assertions: 1.8.0_acorn@8.7.1
- browserslist: 4.21.1
+ browserslist: 4.21.2
chrome-trace-event: 1.0.3
enhanced-resolve: 5.10.0
es-module-lexer: 0.9.3
diff --git a/frontend/src/components/settings/index.tsx b/frontend/src/components/settings/index.tsx
index f9c84c7b..eb3a8bbd 100644
--- a/frontend/src/components/settings/index.tsx
+++ b/frontend/src/components/settings/index.tsx
@@ -1,7 +1,7 @@
import { SidebarNavigation } from 'decky-frontend-lib';
-import GeneralSettings from './pages/GeneralSettings';
-import PluginList from './pages/PluginList';
+import GeneralSettings from './pages/general';
+import PluginList from './pages/plugin_list';
export default function SettingsPage() {
return (
diff --git a/frontend/src/components/settings/pages/general/Updater.tsx b/frontend/src/components/settings/pages/general/Updater.tsx
new file mode 100644
index 00000000..264acca2
--- /dev/null
+++ b/frontend/src/components/settings/pages/general/Updater.tsx
@@ -0,0 +1,86 @@
+import { DialogButton, Field, ProgressBarWithInfo, Spinner, sleep } from 'decky-frontend-lib';
+import { useEffect, useState } from 'react';
+import { FaArrowDown } from 'react-icons/fa';
+
+import { callUpdaterMethod, finishUpdate } from '../../../../updater';
+
+interface VerInfo {
+ current: string;
+ remote: {
+ assets: {
+ browser_download_url: string;
+ created_at: string;
+ }[];
+ name: string;
+ body: string;
+ prerelease: boolean;
+ published_at: string;
+ tag_name: string;
+ } | null;
+ updatable: boolean;
+}
+
+export default function UpdaterSettings() {
+ const [versionInfo, setVersionInfo] = useState<VerInfo | null>(null);
+ const [updateProgress, setUpdateProgress] = useState<number>(-1);
+ const [reloading, setReloading] = useState<boolean>(false);
+ useEffect(() => {
+ (async () => {
+ const res = (await callUpdaterMethod('get_version')) as { result: VerInfo };
+ setVersionInfo(res.result);
+ })();
+ }, []);
+
+ return (
+ <Field
+ label="Updates"
+ description={
+ versionInfo && (
+ <span style={{ whiteSpace: 'pre-line' }}>{`Current version: ${versionInfo.current}\n${
+ versionInfo.updatable ? `Latest version: ${versionInfo.remote?.tag_name}` : ''
+ }`}</span>
+ )
+ }
+ icon={
+ !versionInfo ? (
+ <Spinner style={{ width: '1em', height: 20, display: 'block' }} />
+ ) : (
+ <FaArrowDown style={{ display: 'block' }} />
+ )
+ }
+ >
+ {updateProgress == -1 ? (
+ <DialogButton
+ disabled={
+ !versionInfo?.updatable || !versionInfo?.remote || versionInfo.remote.tag_name == versionInfo.current
+ }
+ onClick={async () => {
+ window.DeckyUpdater = {
+ updateProgress: (i) => {
+ setUpdateProgress(i);
+ },
+ finish: async () => {
+ setUpdateProgress(0);
+ setReloading(true);
+ await finishUpdate();
+ },
+ };
+ setUpdateProgress(0);
+ callUpdaterMethod('do_update');
+ }}
+ >
+ Update
+ </DialogButton>
+ ) : (
+ <ProgressBarWithInfo
+ layout="inline"
+ bottomSeparator={false}
+ nProgress={updateProgress}
+ nTransitionSec={0.01}
+ indeterminate={reloading}
+ sOperationText={reloading ? 'Reloading' : 'Updating'}
+ />
+ )}
+ </Field>
+ );
+}
diff --git a/frontend/src/components/settings/pages/GeneralSettings.tsx b/frontend/src/components/settings/pages/general/index.tsx
index 1cc8076d..7dc5cfa4 100644
--- a/frontend/src/components/settings/pages/GeneralSettings.tsx
+++ b/frontend/src/components/settings/pages/general/index.tsx
@@ -2,7 +2,8 @@ import { DialogButton, Field, TextField } from 'decky-frontend-lib';
import { useState } from 'react';
import { FaShapes } from 'react-icons/fa';
-import { installFromURL } from '../../store/Store';
+import { installFromURL } from '../../../store/Store';
+import UpdaterSettings from './Updater';
export default function GeneralSettings() {
const [pluginURL, setPluginURL] = useState('');
@@ -18,12 +19,15 @@ export default function GeneralSettings() {
onChange={(e) => setChecked(e)}
/>
</Field> */}
+ <UpdaterSettings />
<Field
label="Manual plugin install"
description={<TextField label={'URL'} value={pluginURL} onChange={(e) => setPluginURL(e?.target.value)} />}
icon={<FaShapes style={{ display: 'block' }} />}
>
- <DialogButton onClick={() => installFromURL(pluginURL)}>Install</DialogButton>
+ <DialogButton disabled={pluginURL.length == 0} onClick={() => installFromURL(pluginURL)}>
+ Install
+ </DialogButton>
</Field>
</div>
);
diff --git a/frontend/src/components/settings/pages/PluginList.tsx b/frontend/src/components/settings/pages/plugin_list/index.tsx
index bf01f85a..a554236a 100644
--- a/frontend/src/components/settings/pages/PluginList.tsx
+++ b/frontend/src/components/settings/pages/plugin_list/index.tsx
@@ -1,7 +1,7 @@
import { DialogButton, staticClasses } from 'decky-frontend-lib';
import { FaTrash } from 'react-icons/fa';
-import { useDeckyState } from '../../DeckyState';
+import { useDeckyState } from '../../../DeckyState';
export default function PluginList() {
const { plugins } = useDeckyState();
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx
index 5cf2ed14..364ccb1b 100644
--- a/frontend/src/index.tsx
+++ b/frontend/src/index.tsx
@@ -1,8 +1,10 @@
import PluginLoader from './plugin-loader';
+import { DeckyUpdater } from './updater';
declare global {
interface Window {
DeckyPluginLoader: PluginLoader;
+ DeckyUpdater?: DeckyUpdater;
importDeckyPlugin: Function;
syncDeckyPlugins: Function;
}
diff --git a/frontend/src/updater.ts b/frontend/src/updater.ts
new file mode 100644
index 00000000..692a7a70
--- /dev/null
+++ b/frontend/src/updater.ts
@@ -0,0 +1,30 @@
+import { sleep } from 'decky-frontend-lib';
+
+export enum Branches {
+ Release,
+ Prerelease,
+ Nightly,
+}
+
+export interface DeckyUpdater {
+ updateProgress: (val: number) => void;
+ finish: () => void;
+}
+
+export async function callUpdaterMethod(methodName: string, args = {}) {
+ const response = await fetch(`http://127.0.0.1:1337/updater/${methodName}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(args),
+ });
+
+ return response.json();
+}
+
+export async function finishUpdate() {
+ callUpdaterMethod('do_restart');
+ await sleep(3000);
+ location.reload();
+}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 13b0c350..7e88185b 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -5,6 +5,7 @@
"target": "ES2020",
"jsx": "react",
"jsxFactory": "window.SP_REACT.createElement",
+ "jsxFragmentFactory": "window.SP_REACT.Fragment",
"declaration": false,
"moduleResolution": "node",
"noUnusedLocals": true,