mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-09 23:01:58 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6347ad0856 | |||
| 1377d83023 | |||
| 43d36d2b35 | |||
| 591c58330c | |||
| 501145a210 | |||
| 843e03b42c | |||
| 5f469bfb16 | |||
| acaf6c72e4 | |||
| eb439574be |
+103
-115
@@ -54,13 +54,20 @@ jobs:
|
||||
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data ./backend/static:/static --add-data ./backend/legacy:/legacy ./backend/*.py
|
||||
|
||||
- name: Upload package artifact ⬆️
|
||||
if: ${{ !env.ACT }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PluginLoader
|
||||
path: ./dist/PluginLoader
|
||||
|
||||
- name: Download package artifact locally
|
||||
if: ${{ env.ACT }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ./dist/PluginLoader
|
||||
|
||||
release:
|
||||
name: Release the package
|
||||
name: Release stable version of the package
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'release' }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
@@ -69,144 +76,125 @@ jobs:
|
||||
- name: Checkout 🧰
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Fetch package artifact ⬇️
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: PluginLoader
|
||||
path: dist
|
||||
|
||||
- name: Release 📦
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: Release ${{ steps.tag_version.outputs.new_tag }}
|
||||
tag_name: ${{ steps.tag_version.outputs.new_tag }}
|
||||
files: ./dist/PluginLoader
|
||||
generate_release_notes: true
|
||||
|
||||
prerelease:
|
||||
name: Release the pre-release version of the package
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'prerelease' }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout 🧰
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Fetch package artifact ⬇️
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: PluginLoader
|
||||
path: dist
|
||||
|
||||
- name: Install semver-tool asdf
|
||||
uses: asdf-vm/actions/install@v1
|
||||
with:
|
||||
tool_versions: |
|
||||
semver 3.3.0
|
||||
|
||||
- name: Get tag 🏷️
|
||||
id: old_tag
|
||||
uses: rafarlopes/get-latest-pre-release-tag-action@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Fetch package artifact ⬇️
|
||||
uses: actions/download-artifact@v3
|
||||
if: ${{ !env.ACT }}
|
||||
with:
|
||||
repository: 'decky-loader'
|
||||
name: PluginLoader
|
||||
path: dist
|
||||
|
||||
- name: Get latest release
|
||||
uses: rez0n/actions-github-release@main
|
||||
id: latest_release
|
||||
env:
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repository: "SteamDeckHomebrew/decky-loader"
|
||||
type: "nodraft"
|
||||
|
||||
- name: Prepare tag ⚙️
|
||||
id: ready_tag
|
||||
run: |
|
||||
export VERSION=${{ steps.old_tag.outputs.tag }}
|
||||
export VERSION=${{ steps.latest_release.outputs.release }}
|
||||
echo "VERS: $VERSION"
|
||||
OUT=$(semver bump prerel "$VERSION")
|
||||
echo "OUT: $OUT"
|
||||
OUT="notsemver"
|
||||
if [[ "$VERSION" =~ "-pre" ]]; then
|
||||
printf "is prerelease, bumping to release\n"
|
||||
OUT=$(semver bump release "$VERSION")
|
||||
printf "OUT: ${OUT}\n"
|
||||
elif [[ ! "$VERSION" =~ "-pre" ]]; then
|
||||
printf "previous tag is a release, bumping by a patch\n"
|
||||
OUT=$(semver bump minor "$VERSION")
|
||||
printf "OUT: ${OUT}\n"
|
||||
fi
|
||||
echo "vOUT: v$OUT"
|
||||
echo ::set-output name=tag_name::v$OUT
|
||||
|
||||
- name: Push tag 📤
|
||||
uses: rickstaa/action-create-tag@v1.3.2
|
||||
if: ${{ steps.ready_tag.outputs.tag_name && github.event_name == 'workflow_dispatch' }}
|
||||
if: ${{ steps.ready_tag.outputs.tag_name && github.event_name == 'workflow_dispatch' && !env.ACT }}
|
||||
with:
|
||||
tag: ${{ steps.ready_tag.outputs.tag_name }}
|
||||
message: Pre-release ${{ steps.ready_tag.outputs.tag_name }}
|
||||
|
||||
- name: Release 📦
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && !env.ACT }}
|
||||
with:
|
||||
name: Prerelease ${{ steps.ready_tag.outputs.tag_name }}
|
||||
tag_name: ${{ steps.ready_tag.outputs.tag_name }}
|
||||
files: ./dist/PluginLoader
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
|
||||
prerelease:
|
||||
name: Release the pre-release version of the package
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'prerelease' }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout 🧰
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install semver-tool asdf
|
||||
uses: asdf-vm/actions/install@v1
|
||||
with:
|
||||
tool_versions: |
|
||||
semver 3.3.0
|
||||
|
||||
- name: Fetch package artifact ⬇️
|
||||
uses: actions/download-artifact@v3
|
||||
if: ${{ !env.ACT }}
|
||||
with:
|
||||
name: PluginLoader
|
||||
path: dist
|
||||
|
||||
- name: Get latest release
|
||||
uses: rez0n/actions-github-release@main
|
||||
id: latest_release
|
||||
env:
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repository: "SteamDeckHomebrew/decky-loader"
|
||||
type: "nodraft"
|
||||
|
||||
- name: Prepare tag ⚙️
|
||||
id: ready_tag
|
||||
run: |
|
||||
export VERSION="2.0.5"
|
||||
echo "VERS: $VERSION"
|
||||
OUT=""
|
||||
if [[ ! "$VERSION" =~ "-pre" ]]; then
|
||||
printf "is release, bumping minor version and prerel\n"
|
||||
OUT=$(semver bump minor "$VERSION")
|
||||
OUT="$OUT-pre"
|
||||
OUT=$(semver bump prerel "$OUT")
|
||||
printf "OUT: ${OUT}\n"
|
||||
elif [[ "$VERSION" =~ "-pre" ]]; then
|
||||
printf "is a prerelease, bumping prerel\n"
|
||||
OUT=$(semver bump prerel "$VERSION")
|
||||
printf "OUT: ${OUT}\n"
|
||||
fi
|
||||
echo ::set-output name=tag_name::v$OUT
|
||||
|
||||
- name: Push tag 📤
|
||||
uses: rickstaa/action-create-tag@v1.3.2
|
||||
if: ${{ steps.ready_tag.outputs.tag_name && github.event_name == 'workflow_dispatch' && !env.ACT }}
|
||||
with:
|
||||
tag: ${{ steps.ready_tag.outputs.tag_name }}
|
||||
message: Pre-release ${{ steps.ready_tag.outputs.tag_name }}
|
||||
|
||||
- name: Release 📦
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && !env.ACT }}
|
||||
with:
|
||||
name: Prerelease ${{ steps.ready_tag.outputs.tag_name }}
|
||||
tag_name: ${{ steps.ready_tag.outputs.tag_name }}
|
||||
files: ./dist/PluginLoader
|
||||
prerelease: true
|
||||
generate_release_notes: true
|
||||
|
||||
# nightly:
|
||||
# name: Release the nightly version of the package
|
||||
# if: ${{ github.event_name == 'schedule' }}
|
||||
# needs: build
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - name: Checkout 🧰
|
||||
# uses: actions/checkout@v3
|
||||
|
||||
# - name: Fetch package artifact ⬇️
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: PluginLoader
|
||||
# path: dist
|
||||
|
||||
# - name: Get tag 🏷️
|
||||
# id: old_tag
|
||||
# uses: rafarlopes/get-latest-pre-release-tag-action@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# repository: 'decky-loader'
|
||||
|
||||
# - name: Prepare tag ⚙️
|
||||
# id: ready_tag
|
||||
# run: |
|
||||
# export VERSION=${{ steps.old_tag.outputs.tag }}
|
||||
# export COMMIT=$(git log -1 --pretty=format:%h)
|
||||
# echo ::set-output name=tag_name::$(sed -r 's/(-.*)?-pre$//' <<< $VERSION)-$COMMIT-nightly
|
||||
|
||||
# - name: Push tag 📤
|
||||
# uses: rickstaa/action-create-tag@v1.3.2
|
||||
# if: ${{ steps.ready_tag.outputs.tag_name && github.event_name == 'workflow_dispatch' }}
|
||||
# with:
|
||||
# tag: ${{ steps.ready_tag.outputs.tag_name }}
|
||||
# message: Nightly ${{ steps.ready_tag.outputs.tag_name }}
|
||||
|
||||
# - name: Release 📦
|
||||
# uses: softprops/action-gh-release@v1
|
||||
# if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
# with:
|
||||
# name: Prerelease ${{ steps.ready_tag.outputs.tag_name }}
|
||||
# tag_name: ${{ steps.ready_tag.outputs.tag_name }}
|
||||
# files: ./dist/PluginLoader
|
||||
# prerelease: true
|
||||
# generate_release_notes: true
|
||||
|
||||
# - name: Bump prerelease ⏫
|
||||
# id: bump
|
||||
# if: ${{ github.event_name == 'schedule' }}
|
||||
# run: |
|
||||
# git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||
# echo ::set-output new_tag="nightly-$git_hash"
|
||||
|
||||
# - name: Push tag 📤
|
||||
# uses: rickstaa/action-create-tag@v1.3.2
|
||||
# if: ${{ github.event_name == 'schedule' }}
|
||||
# with:
|
||||
# tag: ${{ steps.bump.outputs.new_tag }}
|
||||
# message: Nightly ${{ steps.bump.outputs.new_tag }}
|
||||
|
||||
# - name: Release 📦
|
||||
# uses: softprops/action-gh-release@v1
|
||||
# if: ${{ github.event_name == 'schedule' }}
|
||||
# with:
|
||||
# name: Nightly ${{ steps.bump.outputs.new_tag }}
|
||||
# tag_name: ${{ steps.bump.outputs.new_tag }}
|
||||
# files: ./dist/PluginLoader
|
||||
# prerelease: true
|
||||
# generate_release_notes: true
|
||||
|
||||
+3
-1
@@ -159,4 +159,6 @@ backend/static
|
||||
.vscode/settings.json
|
||||
|
||||
# plugins folder for local launches
|
||||
plugins/*
|
||||
plugins/*
|
||||
act/.directory
|
||||
act/artifacts/*
|
||||
@@ -0,0 +1,10 @@
|
||||
this directory contains artifacts generated by invocations of https://github.com/nektos/act in order to do local testing of binary builds
|
||||
|
||||
how to?
|
||||
run:
|
||||
|
||||
./act/run-act.sh prerelease
|
||||
|
||||
or
|
||||
|
||||
./act/run-act.sh release
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"inputs": {
|
||||
"release": "prerelease"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"inputs": {
|
||||
"release": "release"
|
||||
}
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
type=$1
|
||||
|
||||
oldartifactsdir="old"
|
||||
|
||||
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
|
||||
cd "$parent_path"
|
||||
|
||||
artifactfolders=$(find artifacts/ -maxdepth 1 -mindepth 1 -type d)
|
||||
if [[ ${#artifactfolders[@]} > 0 ]]; then
|
||||
for i in ${artifactfolders[@]}; do
|
||||
foldername=$(dirname $i)
|
||||
subfoldername=$(basename $i)
|
||||
out=$foldername/$oldartifactsdir/$subfoldername-$(date +'%s')
|
||||
if [[ ! "$subfoldername" =~ "$oldartifactsdir" ]]; then
|
||||
mkdir -p $out
|
||||
mv $i $out
|
||||
printf "Moved "${foldername}"/"${subfoldername}" to "${out}" \n"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
cd ..
|
||||
|
||||
if [[ "$type" == "release" ]]; then
|
||||
printf "release!\n"
|
||||
act workflow_dispatch -e act/release.json --artifact-server-path act/artifacts
|
||||
elif [[ "$type" == "prerelease" ]]; then
|
||||
printf "prerelease!\n"
|
||||
act workflow_dispatch -e act/prerelease.json --artifact-server-path act/artifacts
|
||||
else
|
||||
printf "Release type unspecified/badly specified.\n"
|
||||
printf "Options: 'release' or 'prerelease'\n"
|
||||
fi
|
||||
|
||||
cd act/artifacts
|
||||
|
||||
if [[ -d "1" ]]; then
|
||||
cd "1/artifact"
|
||||
cp "PluginLoader.gz__" "PluginLoader.gz"
|
||||
gzip -d "PluginLoader.gz"
|
||||
chmod +x PluginLoader
|
||||
fi
|
||||
+38
-11
@@ -1,25 +1,26 @@
|
||||
import uuid
|
||||
from logging import getLogger
|
||||
from json.decoder import JSONDecodeError
|
||||
|
||||
from asyncio import sleep
|
||||
from ensurepip import version
|
||||
from json.decoder import JSONDecodeError
|
||||
from logging import getLogger
|
||||
from os import getcwd, path, remove
|
||||
from subprocess import call
|
||||
|
||||
from aiohttp import ClientSession, web
|
||||
|
||||
from injector import inject_to_tab, get_tab
|
||||
|
||||
from os import getcwd, path, remove
|
||||
|
||||
from subprocess import call
|
||||
|
||||
import helpers
|
||||
from injector import get_tab, inject_to_tab
|
||||
from settings import SettingsManager
|
||||
|
||||
logger = getLogger("Updater")
|
||||
|
||||
class Updater:
|
||||
def __init__(self, context) -> None:
|
||||
self.context = context
|
||||
self.settings = self.context.settings
|
||||
# Exposes updater methods to frontend
|
||||
self.updater_methods = {
|
||||
"get_branch": self.get_branch,
|
||||
"get_version": self.get_version,
|
||||
"do_update": self.do_update,
|
||||
"do_restart": self.do_restart,
|
||||
@@ -27,6 +28,13 @@ class Updater:
|
||||
}
|
||||
self.remoteVer = None
|
||||
self.allRemoteVers = None
|
||||
try:
|
||||
self.currentBranch = self.get_branch(self.context.settings)
|
||||
if int(self.currentBranch) == -1:
|
||||
raise ValueError("get_branch could not determine branch!")
|
||||
except:
|
||||
self.currentBranch = 0
|
||||
logger.error("Current branch could not be determined, defaulting to \"Stable\"")
|
||||
try:
|
||||
with open(path.join(getcwd(), ".loader.version"), 'r') as version_file:
|
||||
self.localVer = version_file.readline().replace("\n", "")
|
||||
@@ -55,6 +63,10 @@ class Updater:
|
||||
res["success"] = False
|
||||
return web.json_response(res)
|
||||
|
||||
async def get_branch(self, manager: SettingsManager):
|
||||
logger.debug("current branch: %i" % manager.getSetting("branch", -1))
|
||||
return manager.getSetting("branch", -1)
|
||||
|
||||
async def get_version(self):
|
||||
if self.localVer:
|
||||
return {
|
||||
@@ -67,11 +79,27 @@ class Updater:
|
||||
return {"current": "unknown", "remote": self.remoteVer, "all": self.allRemoteVers, "updatable": False}
|
||||
|
||||
async def check_for_updates(self):
|
||||
logger.debug("checking for updates")
|
||||
selectedBranch = await self.get_branch(self.context.settings)
|
||||
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.allRemoteVers = remoteVersions
|
||||
self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("-pre"), remoteVersions), None)
|
||||
logger.debug("determining release type to find, branch is %i" % selectedBranch)
|
||||
if selectedBranch == 0:
|
||||
logger.debug("release type: release")
|
||||
self.remoteVer = next(filter(lambda ver: ver["tag_name"].startswith("v") and not ver["prerelease"] and ver["tag_name"], remoteVersions), None)
|
||||
elif selectedBranch == 1:
|
||||
logger.debug("release type: pre-release")
|
||||
self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("-pre"), remoteVersions), None)
|
||||
# elif selectedBranch == 2:
|
||||
# logger.debug("release type: nightly")
|
||||
# self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("nightly"), remoteVersions), None)
|
||||
else:
|
||||
logger.error("release type: NOT FOUND")
|
||||
raise ValueError("no valid branch found")
|
||||
# doesn't make it to this line below or farther
|
||||
# logger.debug("Remote Version: %s" % self.remoteVer.find("name"))
|
||||
logger.info("Updated remote version information")
|
||||
tab = await get_tab("SP")
|
||||
await tab.evaluate_js(f"window.DeckyPluginLoader.notifyUpdates()", False, True, False)
|
||||
@@ -88,7 +116,6 @@ class Updater:
|
||||
|
||||
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")
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"decky-frontend-lib": "^1.8.0",
|
||||
"decky-frontend-lib": "^2.0.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"remark-gfm": "^3.0.1"
|
||||
|
||||
Generated
+4
-4
@@ -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.8.0
|
||||
decky-frontend-lib: ^2.0.0
|
||||
husky: ^8.0.1
|
||||
import-sort-style-module: ^6.0.0
|
||||
inquirer: ^8.2.4
|
||||
@@ -27,7 +27,7 @@ specifiers:
|
||||
typescript: ^4.7.4
|
||||
|
||||
dependencies:
|
||||
decky-frontend-lib: 1.8.0
|
||||
decky-frontend-lib: 2.0.0
|
||||
react-icons: 4.4.0_react@16.14.0
|
||||
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
|
||||
remark-gfm: 3.0.1
|
||||
@@ -875,8 +875,8 @@ packages:
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
|
||||
/decky-frontend-lib/1.8.0:
|
||||
resolution: {integrity: sha512-ZPJdbkNI5d/d/19Hv97FVgFyMerYUzwXavYGv8euLMrbH9XuVM4KAx0mbKkO0SQUl75HKQ3LxsaCpA6QLDr9EA==}
|
||||
/decky-frontend-lib/2.0.0:
|
||||
resolution: {integrity: sha512-H7+JpKHlClECVpo+MCEwej7R9wDWk9M2uMSyTvuhTfLZe3RThsxWCiqY640Cjh/zIW2A7GyVRd4SjLtn6Isdeg==}
|
||||
dependencies:
|
||||
minimist: 1.2.6
|
||||
dev: false
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FC, createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { Plugin } from '../plugin';
|
||||
import { PluginUpdateMapping } from '../store';
|
||||
import { VerInfo } from '../updater';
|
||||
|
||||
interface PublicDeckyState {
|
||||
plugins: Plugin[];
|
||||
@@ -9,6 +10,7 @@ interface PublicDeckyState {
|
||||
updates: PluginUpdateMapping | null;
|
||||
hasLoaderUpdate?: boolean;
|
||||
isLoaderUpdating: boolean;
|
||||
versionInfo: VerInfo | null;
|
||||
}
|
||||
|
||||
export class DeckyState {
|
||||
@@ -17,6 +19,7 @@ export class DeckyState {
|
||||
private _updates: PluginUpdateMapping | null = null;
|
||||
private _hasLoaderUpdate: boolean = false;
|
||||
private _isLoaderUpdating: boolean = false;
|
||||
private _versionInfo: VerInfo | null = null;
|
||||
|
||||
public eventBus = new EventTarget();
|
||||
|
||||
@@ -27,9 +30,15 @@ export class DeckyState {
|
||||
updates: this._updates,
|
||||
hasLoaderUpdate: this._hasLoaderUpdate,
|
||||
isLoaderUpdating: this._isLoaderUpdating,
|
||||
versionInfo: this._versionInfo,
|
||||
};
|
||||
}
|
||||
|
||||
setVersionInfo(versionInfo: VerInfo) {
|
||||
this._versionInfo = versionInfo;
|
||||
this.notifyUpdate();
|
||||
}
|
||||
|
||||
setPlugins(plugins: Plugin[]) {
|
||||
this._plugins = plugins;
|
||||
this.notifyUpdate();
|
||||
@@ -66,6 +75,7 @@ export class DeckyState {
|
||||
}
|
||||
|
||||
interface DeckyStateContext extends PublicDeckyState {
|
||||
setVersionInfo(versionInfo: VerInfo): void;
|
||||
setIsLoaderUpdating(hasUpdate: boolean): void;
|
||||
setActivePlugin(name: string): void;
|
||||
closeActivePlugin(): void;
|
||||
@@ -93,12 +103,13 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
|
||||
}, []);
|
||||
|
||||
const setIsLoaderUpdating = (hasUpdate: boolean) => deckyState.setIsLoaderUpdating(hasUpdate);
|
||||
const setVersionInfo = (versionInfo: VerInfo) => deckyState.setVersionInfo(versionInfo);
|
||||
const setActivePlugin = (name: string) => deckyState.setActivePlugin(name);
|
||||
const closeActivePlugin = () => deckyState.closeActivePlugin();
|
||||
|
||||
return (
|
||||
<DeckyStateContext.Provider
|
||||
value={{ ...publicDeckyState, setIsLoaderUpdating, setActivePlugin, closeActivePlugin }}
|
||||
value={{ ...publicDeckyState, setIsLoaderUpdating, setVersionInfo, setActivePlugin, closeActivePlugin }}
|
||||
>
|
||||
{children}
|
||||
</DeckyStateContext.Provider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ToastData, findModule, joinClassNames } from 'decky-frontend-lib';
|
||||
import { FunctionComponent } from 'react';
|
||||
import { ToastData, findModule, findModuleChild, joinClassNames } from 'decky-frontend-lib';
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
interface ToastProps {
|
||||
toast: {
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { Dropdown, Field } from 'decky-frontend-lib';
|
||||
import { FunctionComponent } from 'react';
|
||||
|
||||
import { callUpdaterMethod } from '../../../../updater';
|
||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||
|
||||
enum UpdateBranch {
|
||||
Stable,
|
||||
Prerelease,
|
||||
Nightly,
|
||||
// Nightly,
|
||||
}
|
||||
|
||||
const BranchSelect: FunctionComponent<{}> = () => {
|
||||
const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Prerelease);
|
||||
|
||||
return (
|
||||
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
|
||||
// 0 being stable, 1 being pre-release and 2 being nightly
|
||||
<Field label="Update Channel">
|
||||
<Dropdown
|
||||
rgOptions={Object.values(UpdateBranch)
|
||||
@@ -22,8 +25,10 @@ const BranchSelect: FunctionComponent<{}> = () => {
|
||||
data: UpdateBranch[branch],
|
||||
}))}
|
||||
selectedOption={selectedBranch}
|
||||
onChange={(newVal) => {
|
||||
setSelectedBranch(newVal.data);
|
||||
onChange={async (newVal) => {
|
||||
await setSelectedBranch(newVal.data);
|
||||
callUpdaterMethod('check_for_updates');
|
||||
console.log('switching branches!');
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
@@ -55,20 +55,12 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
||||
}
|
||||
|
||||
export default function UpdaterSettings() {
|
||||
const { isLoaderUpdating, setIsLoaderUpdating } = useDeckyState();
|
||||
const { isLoaderUpdating, setIsLoaderUpdating, versionInfo, setVersionInfo } = useDeckyState();
|
||||
|
||||
const [versionInfo, setVersionInfo] = useState<VerInfo | null>(null);
|
||||
const [checkingForUpdates, setCheckingForUpdates] = useState<boolean>(false);
|
||||
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);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.DeckyUpdater = {
|
||||
updateProgress: (i) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
||||
import { FaShapes } from 'react-icons/fa';
|
||||
|
||||
import { installFromURL } from '../../../../store';
|
||||
// import BranchSelect from './BranchSelect';
|
||||
import BranchSelect from './BranchSelect';
|
||||
import RemoteDebuggingSettings from './RemoteDebugging';
|
||||
import UpdaterSettings from './Updater';
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function GeneralSettings() {
|
||||
/>
|
||||
</Field> */}
|
||||
<UpdaterSettings />
|
||||
{/* <BranchSelect /> */}
|
||||
<BranchSelect />
|
||||
<RemoteDebuggingSettings />
|
||||
<Field
|
||||
label="Manual plugin install"
|
||||
|
||||
@@ -52,10 +52,8 @@ class PluginLoader extends Logger {
|
||||
),
|
||||
icon: (
|
||||
<DeckyStateContextProvider deckyState={this.deckyState}>
|
||||
<>
|
||||
<FaPlug />
|
||||
<TabIcon />
|
||||
</>
|
||||
<FaPlug />
|
||||
<TabIcon />
|
||||
</DeckyStateContextProvider>
|
||||
),
|
||||
});
|
||||
@@ -105,6 +103,7 @@ class PluginLoader extends Logger {
|
||||
|
||||
public async notifyUpdates() {
|
||||
const versionInfo = (await callUpdaterMethod('get_version')).result as VerInfo;
|
||||
this.deckyState.setVersionInfo(versionInfo);
|
||||
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
||||
this.toaster.toast({
|
||||
title: 'Decky',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { afterPatch, findModuleChild, unpatch } from 'decky-frontend-lib';
|
||||
import { Patch, afterPatch, findModuleChild } from 'decky-frontend-lib';
|
||||
import { ReactElement, ReactNode, cloneElement, createElement, memo } from 'react';
|
||||
import type { Route } from 'react-router';
|
||||
|
||||
@@ -22,6 +22,8 @@ class RouterHook extends Logger {
|
||||
private memoizedRouter: any;
|
||||
private gamepadWrapper: any;
|
||||
private routerState: DeckyRouterState = new DeckyRouterState();
|
||||
private wrapperPatch: Patch;
|
||||
private routerPatch?: Patch;
|
||||
|
||||
constructor() {
|
||||
super('RouterHook');
|
||||
@@ -87,7 +89,7 @@ class RouterHook extends Logger {
|
||||
return children;
|
||||
};
|
||||
|
||||
afterPatch(this.gamepadWrapper, 'render', (_: any, ret: any) => {
|
||||
this.wrapperPatch = afterPatch(this.gamepadWrapper, 'render', (_: any, ret: any) => {
|
||||
if (ret?.props?.children?.props?.children?.length == 5) {
|
||||
if (
|
||||
ret.props.children.props.children[2]?.props?.children?.[0]?.type?.type
|
||||
@@ -96,7 +98,7 @@ class RouterHook extends Logger {
|
||||
) {
|
||||
if (!this.router) {
|
||||
this.router = ret.props.children.props.children[2]?.props?.children?.[0]?.type;
|
||||
afterPatch(this.router, 'type', (_: any, ret: any) => {
|
||||
this.routerPatch = afterPatch(this.router, 'type', (_: any, ret: any) => {
|
||||
if (!Route)
|
||||
Route = ret.props.children[0].props.children.find((x: any) => x.props.path == '/createaccount').type;
|
||||
const returnVal = (
|
||||
@@ -133,8 +135,8 @@ class RouterHook extends Logger {
|
||||
}
|
||||
|
||||
deinit() {
|
||||
unpatch(this.gamepadWrapper, 'render');
|
||||
this.router && unpatch(this.router, 'type');
|
||||
this.wrapperPatch.unpatch();
|
||||
this.routerPatch?.unpatch();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { QuickAccessTab, afterPatch, sleep, unpatch } from 'decky-frontend-lib';
|
||||
import { Patch, QuickAccessTab, afterPatch, sleep } from 'decky-frontend-lib';
|
||||
import { memo } from 'react';
|
||||
|
||||
import Logger from './logger';
|
||||
@@ -35,6 +35,8 @@ class TabsHook extends Logger {
|
||||
private qAPTree: any;
|
||||
private rendererTree: any;
|
||||
|
||||
private cNodePatch?: Patch;
|
||||
|
||||
constructor() {
|
||||
super('TabsHook');
|
||||
|
||||
@@ -60,7 +62,7 @@ class TabsHook extends Logger {
|
||||
}
|
||||
let newQA: any;
|
||||
let newQATabRenderer: any;
|
||||
afterPatch(scrollRoot.stateNode, 'render', (_: any, ret: any) => {
|
||||
this.cNodePatch = afterPatch(scrollRoot.stateNode, 'render', (_: any, ret: any) => {
|
||||
if (!this.quickAccess && ret.props.children.props.children[4]) {
|
||||
this.quickAccess = ret?.props?.children?.props?.children[4].type;
|
||||
newQA = (...args: any) => {
|
||||
@@ -103,7 +105,7 @@ class TabsHook extends Logger {
|
||||
}
|
||||
|
||||
deinit() {
|
||||
if (this.cNode) unpatch(this.cNode.stateNode, 'render');
|
||||
this.cNodePatch?.unpatch();
|
||||
if (this.qAPTree) this.qAPTree.type = this.quickAccess;
|
||||
if (this.rendererTree) this.rendererTree.type = this.tabRenderer;
|
||||
if (this.cNode) this.cNode.stateNode.forceUpdate();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ToastData, afterPatch, findInReactTree, findModuleChild, sleep, unpatch } from 'decky-frontend-lib';
|
||||
import { Patch, ToastData, afterPatch, findInReactTree, findModuleChild, sleep } from 'decky-frontend-lib';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import Toast from './components/Toast';
|
||||
import Logger from './logger';
|
||||
@@ -11,7 +12,7 @@ declare global {
|
||||
}
|
||||
|
||||
class Toaster extends Logger {
|
||||
private instanceRet: any;
|
||||
private instanceRetPatch?: Patch;
|
||||
private node: any;
|
||||
private settingsModule: any;
|
||||
|
||||
@@ -43,14 +44,26 @@ class Toaster extends Logger {
|
||||
}
|
||||
|
||||
this.node = instance.return.return;
|
||||
let toast: any;
|
||||
let renderedToast: ReactNode = null;
|
||||
this.node.stateNode.render = (...args: any[]) => {
|
||||
const ret = this.node.stateNode.__proto__.render.call(this.node.stateNode, ...args);
|
||||
if (ret) {
|
||||
this.instanceRet = ret;
|
||||
afterPatch(ret, 'type', (_: any, ret: any) => {
|
||||
if (ret?.props?.children[1]?.children?.props?.notification?.decky) {
|
||||
const toast = ret.props.children[1].children.props.notification;
|
||||
ret.props.children[1].children.type = () => <Toast toast={toast} />;
|
||||
this.instanceRetPatch = afterPatch(ret, 'type', (_: any, ret: any) => {
|
||||
if (ret?.props?.children[1]?.children?.props) {
|
||||
const currentToast = ret.props.children[1].children.props.notification;
|
||||
if (currentToast?.decky) {
|
||||
if (currentToast == toast) {
|
||||
ret.props.children[1].children = renderedToast;
|
||||
} else {
|
||||
toast = currentToast;
|
||||
renderedToast = <Toast toast={toast} />;
|
||||
ret.props.children[1].children = renderedToast;
|
||||
}
|
||||
} else {
|
||||
toast = null;
|
||||
renderedToast = null;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
@@ -80,11 +93,10 @@ class Toaster extends Logger {
|
||||
return;
|
||||
window.NotificationStore.m_rgNotificationToasts.push(toastData);
|
||||
window.NotificationStore.DispatchNextToast();
|
||||
window.NotificationStore.m_rgNotificationToasts.pop();
|
||||
}
|
||||
|
||||
deinit() {
|
||||
this.instanceRet && unpatch(this.instanceRet, 'type');
|
||||
this.instanceRetPatch?.unpatch();
|
||||
this.node && delete this.node.stateNode.render;
|
||||
this.node && this.node.stateNode.forceUpdate();
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@ interface SetSettingArgs<T> {
|
||||
value: T;
|
||||
}
|
||||
|
||||
export function useSetting<T>(key: string, def: T): [value: T | null, setValue: (value: T) => void] {
|
||||
export function useSetting<T>(key: string, def: T): [value: T | null, setValue: (value: T) => Promise<void>] {
|
||||
const [value, setValue] = useState(def);
|
||||
const [ready, setReady] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -20,20 +19,18 @@ export function useSetting<T>(key: string, def: T): [value: T | null, setValue:
|
||||
key,
|
||||
default: def,
|
||||
} as GetSettingArgs<T>)) as { result: T };
|
||||
setReady(true);
|
||||
setValue(res.result);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (ready)
|
||||
(async () => {
|
||||
await window.DeckyPluginLoader.callServerMethod('set_setting', {
|
||||
key,
|
||||
value,
|
||||
} as SetSettingArgs<T>);
|
||||
})();
|
||||
}, [value]);
|
||||
|
||||
return [value, setValue];
|
||||
return [
|
||||
value,
|
||||
async (val: T) => {
|
||||
setValue(val);
|
||||
await window.DeckyPluginLoader.callServerMethod('set_setting', {
|
||||
key,
|
||||
value: val,
|
||||
} as SetSettingArgs<T>);
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user