diff options
Diffstat (limited to 'frontend/src/components')
| -rw-r--r-- | frontend/src/components/Markdown.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/components/patchnotes/InlinePatchNotes.tsx | 21 | ||||
| -rw-r--r-- | frontend/src/components/settings/pages/general/Updater.tsx | 177 |
3 files changed, 159 insertions, 48 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> + </> ); } |
