diff options
| author | AAGaming <aagaming@riseup.net> | 2025-07-01 14:49:48 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-01 14:49:48 -0400 |
| commit | 7b8a18d02d8e6b098f3399d015cf22dd04bc561c (patch) | |
| tree | 7139a04320c70caaaea7a53b77e7b4ff1132ab1a /frontend/src | |
| parent | f327c6c792cb6beb1f32f70045e577a542a48070 (diff) | |
| parent | cbea1518ed1e88fde34e40a16a0e9c20e954a9f5 (diff) | |
| download | decky-loader-7b8a18d02d8e6b098f3399d015cf22dd04bc561c.tar.gz decky-loader-7b8a18d02d8e6b098f3399d015cf22dd04bc561c.zip | |
Merge branch 'main' into aa/fix-updater-reload
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/components/DeckyErrorBoundary.tsx | 82 | ||||
| -rw-r--r-- | frontend/src/components/Markdown.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/components/modals/filepicker/patches/library.ts | 2 | ||||
| -rw-r--r-- | frontend/src/components/settings/pages/developer/index.tsx | 11 | ||||
| -rw-r--r-- | frontend/src/components/settings/pages/general/Updater.tsx | 48 | ||||
| -rw-r--r-- | frontend/src/plugin-loader.tsx | 7 | ||||
| -rw-r--r-- | frontend/src/router-hook.tsx | 10 |
7 files changed, 138 insertions, 31 deletions
diff --git a/frontend/src/components/DeckyErrorBoundary.tsx b/frontend/src/components/DeckyErrorBoundary.tsx index 654db8a0..7a8d2b31 100644 --- a/frontend/src/components/DeckyErrorBoundary.tsx +++ b/frontend/src/components/DeckyErrorBoundary.tsx @@ -4,6 +4,8 @@ import { FunctionComponent, useEffect, useReducer, useState } from 'react'; import { uninstallPlugin } from '../plugin'; import { VerInfo, doRestart, doShutdown } from '../updater'; import { ValveReactErrorInfo, getLikelyErrorSourceFromValveReactError } from '../utils/errors'; +import { useSetting } from '../utils/hooks/useSetting'; +import { UpdateBranch } from './settings/pages/general/BranchSelect'; interface DeckyErrorBoundaryProps { error: ValveReactErrorInfo; @@ -37,6 +39,27 @@ const DeckyErrorBoundary: FunctionComponent<DeckyErrorBoundaryProps> = ({ error, if (!shouldReportToValve) DeckyPluginLoader.errorBoundaryHook.temporarilyDisableReporting(); DeckyPluginLoader.updateVersion().then(setVersionInfo); }, []); + + const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Stable); + const [isChecking, setIsChecking] = useState<boolean>(false); + const [updateProgress, setUpdateProgress] = useState<number>(-1); + const [versionToUpdateTo, setSetVersionToUpdateTo] = useState<string>(''); + + useEffect(() => { + const a = DeckyBackend.addEventListener('updater/update_download_percentage', (percentage) => { + setUpdateProgress(percentage); + }); + + const b = DeckyBackend.addEventListener('updater/finish_download', () => { + setUpdateProgress(-2); + }); + + return () => { + DeckyBackend.removeEventListener('updater/update_download_percentage', a); + DeckyBackend.removeEventListener('updater/finish_download', b); + }; + }, []); + return ( <> <style> @@ -149,6 +172,65 @@ const DeckyErrorBoundary: FunctionComponent<DeckyErrorBoundaryProps> = ({ error, </button> </div> )} + { + <div style={{ display: 'block', marginBottom: '5px' }}> + {updateProgress > -1 + ? 'Update in progress... ' + updateProgress + '%' + : updateProgress == -2 + ? 'Update complete. Restarting...' + : 'Changing your Decky Loader branch and/or \n checking for updates might help!\n'} + {updateProgress == -1 && ( + <div style={{ height: '30px' }}> + <select + style={{ height: '100%' }} + onChange={async (e) => { + const branch = parseInt(e.target.value); + setSelectedBranch(branch); + setSetVersionToUpdateTo(''); + }} + > + <option value="0" selected={selectedBranch == UpdateBranch.Stable}> + Stable + </option> + <option value="1" selected={selectedBranch == UpdateBranch.Prerelease}> + Pre-Release + </option> + <option value="2" selected={selectedBranch == UpdateBranch.Testing}> + Testing + </option> + </select> + <button + style={{ height: '100%' }} + disabled={updateProgress != -1 || isChecking} + onClick={async () => { + if (versionToUpdateTo == '') { + setIsChecking(true); + const versionInfo = (await DeckyBackend.callable( + 'updater/check_for_updates', + )()) as unknown as VerInfo; + setIsChecking(false); + if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) { + setSetVersionToUpdateTo(versionInfo.remote.tag_name); + } else { + setSetVersionToUpdateTo(''); + } + } else { + DeckyBackend.callable('updater/do_update')(); + setUpdateProgress(0); + } + }} + > + {' '} + {isChecking + ? 'Checking for updates...' + : versionToUpdateTo != '' + ? 'Update to ' + versionToUpdateTo + : 'Check for updates'} + </button> + </div> + )} + </div> + } {wasCausedByPlugin && ( <div style={{ display: 'block', marginBottom: '5px' }}> {'\n'} diff --git a/frontend/src/components/Markdown.tsx b/frontend/src/components/Markdown.tsx index cf6657aa..9842750d 100644 --- a/frontend/src/components/Markdown.tsx +++ b/frontend/src/components/Markdown.tsx @@ -1,4 +1,4 @@ -import { Focusable, Navigation } from '@decky/ui'; +import { Focusable, Navigation, findClass, findClassByName } from '@decky/ui'; import { FunctionComponent, useRef } from 'react'; import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown'; import remarkGfm from 'remark-gfm'; @@ -8,6 +8,9 @@ interface MarkdownProps extends ReactMarkdownOptions { } const Markdown: FunctionComponent<MarkdownProps> = (props) => { + const eventDetailsBodyClassName = findClassByName('EventDetailsBody') || undefined; + const eventLinkClassName = findClass('43088', 'Link'); + return ( <Focusable> <ReactMarkdown @@ -25,8 +28,10 @@ const Markdown: FunctionComponent<MarkdownProps> = (props) => { Navigation.NavigateToExternalWeb(aRef.current!.href); }} style={{ display: 'inline' }} + focusClassName="steam-focus" + className={eventDetailsBodyClassName} > - <a ref={aRef} {...nodeProps.node.properties}> + <a ref={aRef} {...nodeProps.node.properties} className={eventLinkClassName}> {nodeProps.children} </a> </Focusable> diff --git a/frontend/src/components/modals/filepicker/patches/library.ts b/frontend/src/components/modals/filepicker/patches/library.ts index 3b7fa679..b0930a5e 100644 --- a/frontend/src/components/modals/filepicker/patches/library.ts +++ b/frontend/src/components/modals/filepicker/patches/library.ts @@ -47,7 +47,7 @@ export default async function libraryPatch() { } const unlisten = History.listen(() => { - if (window.SteamClient.Apps.PromptToChangeShortcut !== patch.patchedFunction) { + if ((window.SteamClient.Apps as any).PromptToChangeShortcut !== patch.patchedFunction) { rePatch(); } }); diff --git a/frontend/src/components/settings/pages/developer/index.tsx b/frontend/src/components/settings/pages/developer/index.tsx index 099f2610..05989806 100644 --- a/frontend/src/components/settings/pages/developer/index.tsx +++ b/frontend/src/components/settings/pages/developer/index.tsx @@ -72,7 +72,16 @@ export default function DeveloperSettings() { } icon={<FaLink style={{ display: 'block' }} />} > - <DialogButton disabled={pluginURL.length == 0} onClick={() => installFromURL(pluginURL)}> + <DialogButton + disabled={pluginURL.length == 0} + onClick={() => { + if (/^https?:\/\//.test(pluginURL)) { + installFromURL(pluginURL); + } else { + installFromURL('https://' + pluginURL); + } + }} + > {t('SettingsDeveloperIndex.third_party_plugins.button_install')} </DialogButton> </Field> diff --git a/frontend/src/components/settings/pages/general/Updater.tsx b/frontend/src/components/settings/pages/general/Updater.tsx index 59756a57..3cd58ab6 100644 --- a/frontend/src/components/settings/pages/general/Updater.tsx +++ b/frontend/src/components/settings/pages/general/Updater.tsx @@ -1,14 +1,4 @@ -import { - Carousel, - DialogButton, - Field, - FocusRing, - Focusable, - ProgressBarWithInfo, - Spinner, - findSP, - showModal, -} from '@decky/ui'; +import { Carousel, DialogButton, Field, Focusable, ProgressBarWithInfo, Spinner, findSP, showModal } from '@decky/ui'; import { Suspense, lazy, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FaExclamation } from 'react-icons/fa'; @@ -23,9 +13,31 @@ const MarkdownRenderer = lazy(() => import('../../../Markdown')); function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) { const SP = findSP(); const { t } = useTranslation(); + return ( - <Focusable onCancelButton={closeModal}> - <FocusRing> + <> + <style> + {` +.steam-focus { + outline-offset: 3px; + outline: 2px solid rgba(255, 255, 255, 0.6); + animation: pulseOutline 1.2s infinite ease-in-out; +} + +@keyframes pulseOutline { + 0% { + outline: 2px solid rgba(255, 255, 255, 0.6); + } + 50% { + outline: 2px solid rgba(255, 255, 255, 1); + } + 100% { + outline: 2px solid rgba(255, 255, 255, 0.6); + } +}`} + </style> + + <Focusable onCancelButton={closeModal}> <Carousel fnItemRenderer={(id: number) => ( <Focusable @@ -35,7 +47,9 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n overflowY: 'scroll', display: 'flex', justifyContent: 'center', - margin: '40px', + margin: '30px', + padding: '0 15px', + backgroundColor: 'rgba(37, 40, 46, 0.5)', }} > <div> @@ -57,11 +71,11 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n nItemMarginX={0} initialColumn={0} autoFocus={true} - fnGetColumnWidth={() => SP.innerWidth} + fnGetColumnWidth={() => SP.innerWidth - SP.innerWidth * (10 / 100)} name={t('Updater.decky_updates') as string} /> - </FocusRing> - </Focusable> + </Focusable> + </> ); } diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx index 88e85a48..df0a6956 100644 --- a/frontend/src/plugin-loader.tsx +++ b/frontend/src/plugin-loader.tsx @@ -1,5 +1,6 @@ import { ToastNotification } from '@decky/api'; import { + EUIMode, ModalRoot, Navigation, PanelSection, @@ -30,7 +31,7 @@ import { HiddenPluginsService } from './hidden-plugins-service'; import Logger from './logger'; import { NotificationService } from './notification-service'; import { InstallType, Plugin, PluginLoadType } from './plugin'; -import RouterHook, { UIMode } from './router-hook'; +import RouterHook from './router-hook'; import { deinitSteamFixes, initSteamFixes } from './steamfixes'; import { checkForPluginUpdates } from './store'; import TabsHook from './tabs-hook'; @@ -205,12 +206,12 @@ class PluginLoader extends Logger { let registration: any; const uiMode = await new Promise( (r) => - (registration = SteamClient.UI.RegisterForUIModeChanged((mode: UIMode) => { + (registration = SteamClient.UI.RegisterForUIModeChanged((mode: EUIMode) => { r(mode); registration.unregister(); })), ); - if (uiMode == UIMode.BigPicture) { + if (uiMode == EUIMode.GamePad) { // wait for SP window to exist before loading plugins while (!findSP()) { await sleep(100); diff --git a/frontend/src/router-hook.tsx b/frontend/src/router-hook.tsx index 9612793a..b3355d76 100644 --- a/frontend/src/router-hook.tsx +++ b/frontend/src/router-hook.tsx @@ -1,4 +1,5 @@ import { + EUIMode, ErrorBoundary, Patch, afterPatch, @@ -31,11 +32,6 @@ declare global { } } -export enum UIMode { - BigPicture = 4, - Desktop = 7, -} - const isPatched = Symbol('is patched'); class RouterHook extends Logger { @@ -76,13 +72,13 @@ class RouterHook extends Logger { this.error('Failed to find router stack module'); } - this.modeChangeRegistration = SteamClient.UI.RegisterForUIModeChanged((mode: UIMode) => { + this.modeChangeRegistration = SteamClient.UI.RegisterForUIModeChanged((mode: EUIMode) => { this.debug(`UI mode changed to ${mode}`); if (this.patchedModes.has(mode)) return; this.patchedModes.add(mode); this.debug(`Patching router for UI mode ${mode}`); switch (mode) { - case UIMode.BigPicture: + case EUIMode.GamePad: this.debug('Patching gamepad router'); this.patchGamepadRouter(); break; |
