summaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
authorAAGaming <aa@mail.catvibers.me>2022-08-26 01:18:28 -0400
committerAAGaming <aa@mail.catvibers.me>2022-08-26 01:18:28 -0400
commitd4d1c2bbabfcec3c62767e614c9d67f516938af2 (patch)
treec30a7643507ade7200eff36e9c16d0512bb1edce /frontend/src
parenteffc4ab0f56119041ac6efecdbf0a782714ec783 (diff)
downloaddecky-loader-d4d1c2bbabfcec3c62767e614c9d67f516938af2.tar.gz
decky-loader-d4d1c2bbabfcec3c62767e614c9d67f516938af2.zip
basic patch notes viewer, lazy-load settings and store, build frontend as esmodule, add lazy-loaded react-markdown, backend changes to accomodate ESModule frontend
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/components/Markdown.tsx9
-rw-r--r--frontend/src/components/patchnotes/InlinePatchNotes.tsx21
-rw-r--r--frontend/src/components/settings/pages/general/Updater.tsx177
-rw-r--r--frontend/src/plugin-loader.tsx42
-rw-r--r--frontend/src/updater.ts25
5 files changed, 210 insertions, 64 deletions
diff --git a/frontend/src/components/Markdown.tsx b/frontend/src/components/Markdown.tsx
new file mode 100644
index 00000000..7b187f14
--- /dev/null
+++ b/frontend/src/components/Markdown.tsx
@@ -0,0 +1,9 @@
+import { FunctionComponent } from 'react';
+import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown';
+import remarkGfm from 'remark-gfm';
+
+const Markdown: FunctionComponent<ReactMarkdownOptions> = (props) => {
+ return <ReactMarkdown remarkPlugins={[remarkGfm]} {...props} />;
+};
+
+export default Markdown;
diff --git a/frontend/src/components/patchnotes/InlinePatchNotes.tsx b/frontend/src/components/patchnotes/InlinePatchNotes.tsx
new file mode 100644
index 00000000..84861c04
--- /dev/null
+++ b/frontend/src/components/patchnotes/InlinePatchNotes.tsx
@@ -0,0 +1,21 @@
+import { Focusable, updaterFieldClasses } from 'decky-frontend-lib';
+import { FunctionComponent, ReactNode } from 'react';
+
+interface InlinePatchNotesProps {
+ date: ReactNode;
+ title: string;
+ children: ReactNode;
+ onClick?: () => void;
+}
+
+const InlinePatchNotes: FunctionComponent<InlinePatchNotesProps> = ({ date, title, children, onClick }) => {
+ return (
+ <Focusable className={updaterFieldClasses.PatchNotes} onActivate={onClick}>
+ <div className={updaterFieldClasses.PostedTime}>{date}</div>
+ <div className={updaterFieldClasses.EventDetailTitle}>{title}</div>
+ <div className={updaterFieldClasses.EventDetailsBody}>{children}</div>
+ </Focusable>
+ );
+};
+
+export default InlinePatchNotes;
diff --git a/frontend/src/components/settings/pages/general/Updater.tsx b/frontend/src/components/settings/pages/general/Updater.tsx
index f7e4a29d..cb58b24c 100644
--- a/frontend/src/components/settings/pages/general/Updater.tsx
+++ b/frontend/src/components/settings/pages/general/Updater.tsx
@@ -1,9 +1,66 @@
-import { DialogButton, Field, ProgressBarWithInfo, Spinner } from 'decky-frontend-lib';
+import { Carousel, DialogButton, Field, Focusable, ProgressBarWithInfo, Spinner, showModal } from 'decky-frontend-lib';
+import { useCallback } from 'react';
+import { Suspense, lazy } from 'react';
import { useEffect, useState } from 'react';
import { FaArrowDown } from 'react-icons/fa';
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
import { useDeckyState } from '../../../DeckyState';
+import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
+
+const MarkdownRenderer = lazy(() => import('../../../Markdown'));
+
+// import ReactMarkdown from 'react-markdown'
+// import remarkGfm from 'remark-gfm'
+
+function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
+ return (
+ <Focusable onCancelButton={closeModal}>
+ <Carousel
+ fnItemRenderer={(id: number, ...args: any[]) => {
+ console.log(args, versionInfo);
+ return (
+ <Focusable
+ onActivate={() => {}}
+ style={{
+ marginTop: '40px',
+ height: 'calc( 100% - 40px )',
+ overflowY: 'scroll',
+ display: 'flex',
+ justifyContent: 'center',
+ margin: '40px',
+ }}
+ >
+ <div>
+ <h1>{versionInfo?.all?.[id]?.name}</h1>
+ {versionInfo?.all?.[id]?.body ? (
+ <Suspense fallback={<Spinner style={{ width: '24', height: '24' }} />}>
+ <MarkdownRenderer>{versionInfo.all[id].body}</MarkdownRenderer>
+ </Suspense>
+ ) : (
+ 'no patch notes for this version'
+ )}
+ </div>
+ </Focusable>
+ );
+ }}
+ fnGetId={(id) => {
+ return id;
+ }}
+ nNumItems={versionInfo?.all?.length}
+ nHeight={window.innerHeight - 150}
+ nItemHeight={window.innerHeight - 200}
+ nItemMarginX={0}
+ initialColumn={0}
+ autoFocus={true}
+ fnGetColumnWidth={(...args: any[]) => {
+ console.log('cw', args);
+ return window.innerWidth;
+ }}
+ />
+ </Focusable>
+ );
+}
export default function UpdaterSettings() {
const { isLoaderUpdating, setIsLoaderUpdating } = useDeckyState();
@@ -34,56 +91,80 @@ export default function UpdaterSettings() {
};
}, []);
+ const showPatchNotes = useCallback(() => {
+ showModal(<PatchNotesModal versionInfo={versionInfo} />);
+ }, [versionInfo]);
+
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' }} />
+ <>
+ <Field
+ onOptionsActionDescription="Patch Notes"
+ onOptionsButton={showPatchNotes}
+ 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 && !isLoaderUpdating ? (
+ <DialogButton
+ disabled={!versionInfo?.updatable || checkingForUpdates}
+ onClick={
+ !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
+ ? async () => {
+ setCheckingForUpdates(true);
+ const res = (await callUpdaterMethod('check_for_updates')) as { result: VerInfo };
+ setVersionInfo(res.result);
+ setCheckingForUpdates(false);
+ }
+ : async () => {
+ setUpdateProgress(0);
+ callUpdaterMethod('do_update');
+ }
+ }
+ >
+ {checkingForUpdates
+ ? 'Checking'
+ : !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
+ ? 'Check For Updates'
+ : 'Install Update'}
+ </DialogButton>
) : (
- <FaArrowDown style={{ display: 'block' }} />
- )
- }
- >
- {updateProgress == -1 && !isLoaderUpdating ? (
- <DialogButton
- disabled={!versionInfo?.updatable || checkingForUpdates}
- onClick={
- !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
- ? async () => {
- setCheckingForUpdates(true);
- const res = (await callUpdaterMethod('check_for_updates')) as { result: VerInfo };
- setVersionInfo(res.result);
- setCheckingForUpdates(false);
- }
- : async () => {
- setUpdateProgress(0);
- callUpdaterMethod('do_update');
- }
- }
+ <ProgressBarWithInfo
+ layout="inline"
+ bottomSeparator={false}
+ nProgress={updateProgress}
+ indeterminate={reloading}
+ sOperationText={reloading ? 'Reloading' : 'Updating'}
+ />
+ )}
+ </Field>
+ {versionInfo?.remote && (
+ <InlinePatchNotes
+ title={versionInfo?.remote.name}
+ date={new Intl.RelativeTimeFormat('en-US', {
+ numeric: 'auto',
+ }).format(
+ Math.ceil((new Date(versionInfo.remote.published_at).getTime() - new Date().getTime()) / 86400000),
+ 'day',
+ )}
+ onClick={showPatchNotes}
>
- {checkingForUpdates
- ? 'Checking'
- : !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
- ? 'Check For Updates'
- : 'Install Update'}
- </DialogButton>
- ) : (
- <ProgressBarWithInfo
- layout="inline"
- bottomSeparator={false}
- nProgress={updateProgress}
- indeterminate={reloading}
- sOperationText={reloading ? 'Reloading' : 'Updating'}
- />
+ <Suspense fallback={<Spinner style={{ width: '24', height: '24' }} />}>
+ <MarkdownRenderer>{versionInfo?.remote.body}</MarkdownRenderer>
+ </Suspense>
+ </InlinePatchNotes>
)}
- </Field>
+ </>
);
}
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index c0fc3d00..661a2f67 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -1,4 +1,5 @@
-import { ModalRoot, QuickAccessTab, Router, showModal, sleep, staticClasses } from 'decky-frontend-lib';
+import { ModalRoot, QuickAccessTab, Router, SteamSpinner, showModal, sleep, staticClasses } from 'decky-frontend-lib';
+import { Suspense, lazy } from 'react';
import { FaPlug } from 'react-icons/fa';
import { DeckyState, DeckyStateContextProvider, useDeckyState } from './components/DeckyState';
@@ -6,8 +7,6 @@ import LegacyPlugin from './components/LegacyPlugin';
import PluginInstallModal from './components/modals/PluginInstallModal';
import NotificationBadge from './components/NotificationBadge';
import PluginView from './components/PluginView';
-import SettingsPage from './components/settings';
-import StorePage from './components/store/Store';
import TitleView from './components/TitleView';
import Logger from './logger';
import { Plugin } from './plugin';
@@ -61,11 +60,44 @@ class PluginLoader extends Logger {
),
});
- this.routerHook.addRoute('/decky/store', () => <StorePage />);
+ const StorePage = lazy(() => import('./components/store/Store'));
+ const SettingsPage = lazy(() => import('./components/settings'));
+
+ this.routerHook.addRoute('/decky/store', () => (
+ <Suspense
+ fallback={
+ <div
+ style={{
+ marginTop: '40px',
+ height: 'calc( 100% - 40px )',
+ overflowY: 'scroll',
+ }}
+ >
+ <SteamSpinner />
+ </div>
+ }
+ >
+ <StorePage />
+ </Suspense>
+ ));
this.routerHook.addRoute('/decky/settings', () => {
return (
<DeckyStateContextProvider deckyState={this.deckyState}>
- <SettingsPage />
+ <Suspense
+ fallback={
+ <div
+ style={{
+ marginTop: '40px',
+ height: 'calc( 100% - 40px )',
+ overflowY: 'scroll',
+ }}
+ >
+ <SteamSpinner />
+ </div>
+ }
+ >
+ <SettingsPage />
+ </Suspense>
</DeckyStateContextProvider>
);
});
diff --git a/frontend/src/updater.ts b/frontend/src/updater.ts
index dd37f0b4..ff9cb591 100644
--- a/frontend/src/updater.ts
+++ b/frontend/src/updater.ts
@@ -11,19 +11,22 @@ export interface DeckyUpdater {
finish: () => void;
}
+export interface RemoteVerInfo {
+ assets: {
+ browser_download_url: string;
+ created_at: string;
+ }[];
+ name: string;
+ body: string;
+ prerelease: boolean;
+ published_at: string;
+ tag_name: string;
+}
+
export 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;
+ remote: RemoteVerInfo | null;
+ all: RemoteVerInfo[] | null;
updatable: boolean;
}