mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-11 17:12:01 +00:00
fix updater for new installs, fix file picker patch, fix scrolling on patch notes, fix tasks dir
This commit is contained in:
Vendored
+1
-1
@@ -107,7 +107,7 @@
|
|||||||
"type": "shell",
|
"type": "shell",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"dependsOn" : ["checkforsettings"],
|
"dependsOn" : ["checkforsettings"],
|
||||||
"command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'export PLUGIN_PATH=${config:deckdir}/homebrew/dev/plugins; export CHOWN_PLUGIN_PATH=0; echo '${config:deckpass}' | sudo -SE python3 ${config:deckdir}/homebrew/dev/pluginloader/backend/main.py'",
|
"command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'export PLUGIN_PATH=${config:deckdir}/homebrew/dev/plugins; export CHOWN_PLUGIN_PATH=0; cd ${config:deckdir}/homebrew/services; echo '${config:deckpass}' | sudo -SE python3 ${config:deckdir}/homebrew/dev/pluginloader/backend/main.py'",
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+18
-9
@@ -29,18 +29,18 @@ class Updater:
|
|||||||
self.remoteVer = None
|
self.remoteVer = None
|
||||||
self.allRemoteVers = None
|
self.allRemoteVers = None
|
||||||
try:
|
try:
|
||||||
self.currentBranch = self.get_branch(self.context.settings)
|
logger.info(getcwd())
|
||||||
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:
|
with open(path.join(getcwd(), ".loader.version"), 'r') as version_file:
|
||||||
self.localVer = version_file.readline().replace("\n", "")
|
self.localVer = version_file.readline().replace("\n", "")
|
||||||
except:
|
except:
|
||||||
self.localVer = False
|
self.localVer = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.currentBranch = self.get_branch(self.context.settings)
|
||||||
|
except:
|
||||||
|
self.currentBranch = 0
|
||||||
|
logger.error("Current branch could not be determined, defaulting to \"Stable\"")
|
||||||
|
|
||||||
if context:
|
if context:
|
||||||
context.web_app.add_routes([
|
context.web_app.add_routes([
|
||||||
web.post("/updater/{method_name}", self._handle_server_method_call)
|
web.post("/updater/{method_name}", self._handle_server_method_call)
|
||||||
@@ -64,8 +64,17 @@ class Updater:
|
|||||||
return web.json_response(res)
|
return web.json_response(res)
|
||||||
|
|
||||||
def get_branch(self, manager: SettingsManager):
|
def get_branch(self, manager: SettingsManager):
|
||||||
logger.debug("current branch: %i" % manager.getSetting("branch", 0))
|
ver = manager.getSetting("branch", -1)
|
||||||
return manager.getSetting("branch", 0)
|
logger.debug("current branch: %i" % ver)
|
||||||
|
if ver == -1:
|
||||||
|
logger.info("Current branch is not set, determining branch from version...")
|
||||||
|
if self.localVer.startswith("v") and self.localVer.find("-pre"):
|
||||||
|
logger.info("Current version determined to be pre-release")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
logger.info("Current version determined to be stable")
|
||||||
|
return 0
|
||||||
|
return ver
|
||||||
|
|
||||||
async def _get_branch(self, manager: SettingsManager):
|
async def _get_branch(self, manager: SettingsManager):
|
||||||
return self.get_branch(manager)
|
return self.get_branch(manager)
|
||||||
|
|||||||
@@ -1,9 +1,42 @@
|
|||||||
import { FunctionComponent } from 'react';
|
import { Focusable } from 'decky-frontend-lib';
|
||||||
|
import { FunctionComponent, useRef } from 'react';
|
||||||
import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown';
|
import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|
||||||
const Markdown: FunctionComponent<ReactMarkdownOptions> = (props) => {
|
interface MarkdownProps extends ReactMarkdownOptions {
|
||||||
return <ReactMarkdown remarkPlugins={[remarkGfm]} {...props} />;
|
onDismiss?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Markdown: FunctionComponent<MarkdownProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<Focusable>
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
components={{
|
||||||
|
div: (nodeProps) => <Focusable {...nodeProps.node.properties}>{nodeProps.children}</Focusable>,
|
||||||
|
a: (nodeProps) => {
|
||||||
|
console.log(nodeProps.node, nodeProps);
|
||||||
|
const aRef = useRef<HTMLAnchorElement>(null);
|
||||||
|
return (
|
||||||
|
// TODO fix focus ring
|
||||||
|
<Focusable
|
||||||
|
onActivate={() => {}}
|
||||||
|
onOKButton={() => {
|
||||||
|
aRef?.current?.click();
|
||||||
|
props.onDismiss?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a ref={aRef} {...nodeProps.node.properties}>
|
||||||
|
{nodeProps.children}
|
||||||
|
</a>
|
||||||
|
</Focusable>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Focusable>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Markdown;
|
export default Markdown;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { SteamSpinner } from 'decky-frontend-lib';
|
import { Focusable, SteamSpinner } from 'decky-frontend-lib';
|
||||||
import { FunctionComponent, ReactElement, ReactNode, Suspense } from 'react';
|
import { FunctionComponent, ReactElement, ReactNode, Suspense } from 'react';
|
||||||
|
|
||||||
interface WithSuspenseProps {
|
interface WithSuspenseProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
route?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nice little wrapper around Suspense so we don't have to duplicate the styles and code for the loading spinner
|
// Nice little wrapper around Suspense so we don't have to duplicate the styles and code for the loading spinner
|
||||||
@@ -13,15 +14,20 @@ const WithSuspense: FunctionComponent<WithSuspenseProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
<div
|
<Focusable
|
||||||
|
// needed to enable focus ring so that the focus properly resets on load
|
||||||
|
onActivate={() => {}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: '40px',
|
|
||||||
height: 'calc( 100% - 40px )',
|
|
||||||
overflowY: 'scroll',
|
overflowY: 'scroll',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
...(props.route && {
|
||||||
|
marginTop: '40px',
|
||||||
|
height: 'calc( 100% - 40px )',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SteamSpinner />
|
<SteamSpinner />
|
||||||
</div>
|
</Focusable>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ const FilePicker: FunctionComponent<FilePickerProps> = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newPathArr = path.split('/');
|
const newPathArr = path.split('/');
|
||||||
newPathArr.pop();
|
newPathArr.pop();
|
||||||
const newPath = newPathArr.join('/');
|
let newPath = newPathArr.join('/');
|
||||||
|
if (newPath == '') newPath = '/';
|
||||||
setPath(newPath);
|
setPath(newPath);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -113,7 +114,7 @@ const FilePicker: FunctionComponent<FilePickerProps> = ({
|
|||||||
<DialogButton
|
<DialogButton
|
||||||
style={{ borderRadius: 'unset', margin: '0', padding: '10px' }}
|
style={{ borderRadius: 'unset', margin: '0', padding: '10px' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const fullPath = `${path}/${file.name}`;
|
const fullPath = `${path}${path.endsWith('/') ? '' : '/'}${file.name}`;
|
||||||
if (file.isdir) setPath(fullPath);
|
if (file.isdir) setPath(fullPath);
|
||||||
else {
|
else {
|
||||||
onSubmit({ path: fullPath, realpath: file.realpath });
|
onSubmit({ path: fullPath, realpath: file.realpath });
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Patch, replacePatch, sleep } from 'decky-frontend-lib';
|
import { Patch, findModuleChild, replacePatch } from 'decky-frontend-lib';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -10,8 +10,7 @@ declare global {
|
|||||||
let patch: Patch;
|
let patch: Patch;
|
||||||
|
|
||||||
function rePatch() {
|
function rePatch() {
|
||||||
// If you patch anything on SteamClient within the first few seconds of the client having loaded it will get redefined for some reason, so repatch any of these changes that occur within the first minute of the client loading
|
// If you patch anything on SteamClient within the first few seconds of the client having loaded it will get redefined for some reason, so repatch any of these changes that occur within the first 20s of the last patch
|
||||||
patch?.unpatch();
|
|
||||||
patch = replacePatch(window.SteamClient.Apps, 'PromptToChangeShortcut', async ([appid]: number[]) => {
|
patch = replacePatch(window.SteamClient.Apps, 'PromptToChangeShortcut', async ([appid]: number[]) => {
|
||||||
try {
|
try {
|
||||||
const details = window.appDetailsStore.GetAppDetails(appid);
|
const details = window.appDetailsStore.GetAppDetails(appid);
|
||||||
@@ -30,13 +29,29 @@ function rePatch() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function libraryPatch() {
|
// TODO type and add to frontend-lib
|
||||||
await sleep(10000);
|
const History = findModuleChild((m) => {
|
||||||
rePatch();
|
if (typeof m !== 'object') return undefined;
|
||||||
await sleep(10000);
|
for (let prop in m) {
|
||||||
rePatch();
|
if (m[prop]?.m_history) return m[prop].m_history;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
export default async function libraryPatch() {
|
||||||
patch.unpatch();
|
try {
|
||||||
};
|
rePatch();
|
||||||
|
const unlisten = History.listen(() => {
|
||||||
|
if (window.SteamClient.Apps.PromptToChangeShortcut !== patch.patchedFunction) {
|
||||||
|
rePatch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
patch.unpatch();
|
||||||
|
unlisten();
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error patching library file picker', e);
|
||||||
|
}
|
||||||
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,19 +7,16 @@ import { FaArrowDown } from 'react-icons/fa';
|
|||||||
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
|
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
|
||||||
import { useDeckyState } from '../../../DeckyState';
|
import { useDeckyState } from '../../../DeckyState';
|
||||||
import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
|
import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
|
||||||
|
import WithSuspense from '../../../WithSuspense';
|
||||||
|
|
||||||
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
|
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
|
||||||
|
|
||||||
// import ReactMarkdown from 'react-markdown'
|
|
||||||
// import remarkGfm from 'remark-gfm'
|
|
||||||
|
|
||||||
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
|
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
|
||||||
return (
|
return (
|
||||||
<Focusable onCancelButton={closeModal}>
|
<Focusable onCancelButton={closeModal}>
|
||||||
<Carousel
|
<Carousel
|
||||||
fnItemRenderer={(id: number) => (
|
fnItemRenderer={(id: number) => (
|
||||||
<Focusable
|
<Focusable
|
||||||
onActivate={() => {}}
|
|
||||||
style={{
|
style={{
|
||||||
marginTop: '40px',
|
marginTop: '40px',
|
||||||
height: 'calc( 100% - 40px )',
|
height: 'calc( 100% - 40px )',
|
||||||
@@ -32,9 +29,9 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
|||||||
<div>
|
<div>
|
||||||
<h1>{versionInfo?.all?.[id]?.name}</h1>
|
<h1>{versionInfo?.all?.[id]?.name}</h1>
|
||||||
{versionInfo?.all?.[id]?.body ? (
|
{versionInfo?.all?.[id]?.body ? (
|
||||||
<Suspense fallback={<Spinner style={{ width: '24', height: '24' }} />}>
|
<WithSuspense>
|
||||||
<MarkdownRenderer>{versionInfo.all[id].body}</MarkdownRenderer>
|
<MarkdownRenderer onDismiss={closeModal}>{versionInfo.all[id].body}</MarkdownRenderer>
|
||||||
</Suspense>
|
</WithSuspense>
|
||||||
) : (
|
) : (
|
||||||
'no patch notes for this version'
|
'no patch notes for this version'
|
||||||
)}
|
)}
|
||||||
@@ -43,8 +40,8 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
|
|||||||
)}
|
)}
|
||||||
fnGetId={(id) => id}
|
fnGetId={(id) => id}
|
||||||
nNumItems={versionInfo?.all?.length}
|
nNumItems={versionInfo?.all?.length}
|
||||||
nHeight={window.innerHeight - 150}
|
nHeight={window.innerHeight - 40}
|
||||||
nItemHeight={window.innerHeight - 200}
|
nItemHeight={window.innerHeight - 40}
|
||||||
nItemMarginX={0}
|
nItemMarginX={0}
|
||||||
initialColumn={0}
|
initialColumn={0}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ class PluginLoader extends Logger {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.routerHook.addRoute('/decky/store', () => (
|
this.routerHook.addRoute('/decky/store', () => (
|
||||||
<WithSuspense>
|
<WithSuspense route={true}>
|
||||||
<StorePage />
|
<StorePage />
|
||||||
</WithSuspense>
|
</WithSuspense>
|
||||||
));
|
));
|
||||||
this.routerHook.addRoute('/decky/settings', () => {
|
this.routerHook.addRoute('/decky/settings', () => {
|
||||||
return (
|
return (
|
||||||
<DeckyStateContextProvider deckyState={this.deckyState}>
|
<DeckyStateContextProvider deckyState={this.deckyState}>
|
||||||
<WithSuspense>
|
<WithSuspense route={true}>
|
||||||
<SettingsPage />
|
<SettingsPage />
|
||||||
</WithSuspense>
|
</WithSuspense>
|
||||||
</DeckyStateContextProvider>
|
</DeckyStateContextProvider>
|
||||||
@@ -81,11 +81,19 @@ class PluginLoader extends Logger {
|
|||||||
});
|
});
|
||||||
|
|
||||||
initFilepickerPatches();
|
initFilepickerPatches();
|
||||||
|
|
||||||
|
this.updateVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateVersion() {
|
||||||
|
const versionInfo = (await callUpdaterMethod('get_version')).result as VerInfo;
|
||||||
|
this.deckyState.setVersionInfo(versionInfo);
|
||||||
|
|
||||||
|
return versionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async notifyUpdates() {
|
public async notifyUpdates() {
|
||||||
const versionInfo = (await callUpdaterMethod('get_version')).result as VerInfo;
|
const versionInfo = await this.updateVersion();
|
||||||
this.deckyState.setVersionInfo(versionInfo);
|
|
||||||
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
||||||
this.toaster.toast({
|
this.toaster.toast({
|
||||||
title: 'Decky',
|
title: 'Decky',
|
||||||
|
|||||||
Reference in New Issue
Block a user