summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/decky_loader/browser.py4
-rw-r--r--backend/decky_loader/locales/en-US.json25
-rw-r--r--frontend/src/components/DeckyState.tsx12
-rw-r--r--frontend/src/components/PluginView.tsx44
-rw-r--r--frontend/src/components/modals/MultiplePluginsInstallModal.tsx37
-rw-r--r--frontend/src/components/modals/PluginInstallModal.tsx66
-rw-r--r--frontend/src/components/store/PluginCard.tsx73
-rw-r--r--frontend/src/components/store/Store.tsx10
-rw-r--r--frontend/src/plugin-loader.tsx8
-rw-r--r--frontend/src/plugin.ts12
-rw-r--r--frontend/src/utils/TranslationHelper.tsx19
11 files changed, 211 insertions, 99 deletions
diff --git a/backend/decky_loader/browser.py b/backend/decky_loader/browser.py
index aa108c59..64ecfb78 100644
--- a/backend/decky_loader/browser.py
+++ b/backend/decky_loader/browser.py
@@ -29,6 +29,8 @@ class PluginInstallType(IntEnum):
INSTALL = 0
REINSTALL = 1
UPDATE = 2
+ DOWNGRADE = 3
+ OVERWRITE = 4
class PluginInstallRequest(TypedDict):
name: str
@@ -323,5 +325,5 @@ class PluginBrowser:
if name in plugin_order:
plugin_order.remove(name)
self.settings.setSetting("pluginOrder", plugin_order)
-
+
logger.debug("Removed any settings for plugin %s", name)
diff --git a/backend/decky_loader/locales/en-US.json b/backend/decky_loader/locales/en-US.json
index 23566026..7cbc2a8b 100644
--- a/backend/decky_loader/locales/en-US.json
+++ b/backend/decky_loader/locales/en-US.json
@@ -52,7 +52,9 @@
"MultiplePluginsInstallModal": {
"confirm": "Are you sure you want to make the following modifications?",
"description": {
+ "downgrade": "Downgrade {{name}} to {{version}}",
"install": "Install {{name}} {{version}}",
+ "overwrite": "Overwrite {{name}} with {{version}}",
"reinstall": "Reinstall {{name}} {{version}}",
"update": "Update {{name}} to {{version}}"
},
@@ -61,10 +63,14 @@
"loading": "Working"
},
"title": {
+ "downgrade_one": "Downgrade 1 plugin",
+ "downgrade_other": "Downgrade {{count}} plugins",
"install_one": "Install 1 plugin",
"install_other": "Install {{count}} plugins",
"mixed_one": "Modify {{count}} plugin",
"mixed_other": "Modify {{count}} plugins",
+ "overwrite_one": "Overwrite 1 plugin",
+ "overwrite_other": "Overwrite {{count}} plugins",
"reinstall_one": "Reinstall 1 plugin",
"reinstall_other": "Reinstall {{count}} plugins",
"update_one": "Update 1 plugin",
@@ -72,12 +78,22 @@
}
},
"PluginCard": {
+ "plugin_downgrade": "Downgrade",
"plugin_full_access": "This plugin has full access to your Steam Deck.",
"plugin_install": "Install",
"plugin_no_desc": "No description provided.",
+ "plugin_overwrite": "Overwrite",
+ "plugin_reinstall": "Reinstall",
+ "plugin_update": "Update",
"plugin_version_label": "Plugin Version"
},
"PluginInstallModal": {
+ "downgrade": {
+ "button_idle": "Downgrade",
+ "button_processing": "Downgrading",
+ "desc": "Are you sure you want to downgrade {{artifact}} to version {{version}}?",
+ "title": "Downgrade {{artifact}}"
+ },
"install": {
"button_idle": "Install",
"button_processing": "Installing",
@@ -85,6 +101,13 @@
"title": "Install {{artifact}}"
},
"no_hash": "This plugin does not have a hash, you are installing it at your own risk.",
+ "not_installed": "(not installed)",
+ "overwrite": {
+ "button_idle": "Overwrite",
+ "button_processing": "Overwriting",
+ "desc": "Are you sure you want to overwrite {{artifact}} with version {{version}}?",
+ "title": "Overwrite {{artifact}}"
+ },
"reinstall": {
"button_idle": "Reinstall",
"button_processing": "Reinstalling",
@@ -94,7 +117,7 @@
"update": {
"button_idle": "Update",
"button_processing": "Updating",
- "desc": "Are you sure you want to update {{artifact}} {{version}}?",
+ "desc": "Are you sure you want to update {{artifact}} to version {{version}}?",
"title": "Update {{artifact}}"
}
},
diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx
index 75106e62..d2ac63ae 100644
--- a/frontend/src/components/DeckyState.tsx
+++ b/frontend/src/components/DeckyState.tsx
@@ -128,9 +128,17 @@ interface DeckyStateContext extends PublicDeckyState {
closeActivePlugin(): void;
}
-const DeckyStateContext = createContext<DeckyStateContext>(null as any);
+const DeckyStateContext = createContext<DeckyStateContext | null>(null);
-export const useDeckyState = () => useContext(DeckyStateContext);
+export const useDeckyState = () => {
+ const deckyState = useContext(DeckyStateContext);
+
+ if (deckyState === null) {
+ throw new Error('useDeckyState needs a parent DeckyStateContext');
+ }
+
+ return deckyState;
+};
interface Props {
deckyState: DeckyState;
diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx
index 19afbca5..1d39972e 100644
--- a/frontend/src/components/PluginView.tsx
+++ b/frontend/src/components/PluginView.tsx
@@ -1,27 +1,26 @@
import { ButtonItem, ErrorBoundary, Focusable, PanelSection, PanelSectionRow } from '@decky/ui';
-import { FC, useEffect, useState } from 'react';
+import { FC, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash } from 'react-icons/fa';
-import { Plugin } from '../plugin';
import { useDeckyState } from './DeckyState';
import NotificationBadge from './NotificationBadge';
import { useQuickAccessVisible } from './QuickAccessVisibleState';
import TitleView from './TitleView';
const PluginView: FC = () => {
- const { hiddenPlugins } = useDeckyState();
- const { plugins, updates, activePlugin, pluginOrder, setActivePlugin, closeActivePlugin } = useDeckyState();
+ const { plugins, hiddenPlugins, updates, activePlugin, pluginOrder, setActivePlugin, closeActivePlugin } =
+ useDeckyState();
const visible = useQuickAccessVisible();
const { t } = useTranslation();
- const [pluginList, setPluginList] = useState<Plugin[]>(
- plugins.sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name)),
- );
-
- useEffect(() => {
- setPluginList(plugins.sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name)));
+ const pluginList = useMemo(() => {
console.log('updating PluginView after changes');
+
+ return [...plugins]
+ .sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name))
+ .filter((p) => p.content)
+ .filter(({ name }) => !hiddenPlugins.includes(name));
}, [plugins, pluginOrder]);
if (activePlugin) {
@@ -43,20 +42,17 @@ const PluginView: FC = () => {
}}
>
<PanelSection>
- {pluginList
- .filter((p) => p.content)
- .filter(({ name }) => !hiddenPlugins.includes(name))
- .map(({ name, icon }) => (
- <PanelSectionRow key={name}>
- <ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
- {icon}
- <div>{name}</div>
- <NotificationBadge show={updates?.has(name)} style={{ top: '-5px', right: '-5px' }} />
- </div>
- </ButtonItem>
- </PanelSectionRow>
- ))}
+ {pluginList.map(({ name, icon }) => (
+ <PanelSectionRow key={name}>
+ <ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
+ {icon}
+ <div>{name}</div>
+ <NotificationBadge show={updates?.has(name)} style={{ top: '-5px', right: '-5px' }} />
+ </div>
+ </ButtonItem>
+ </PanelSectionRow>
+ ))}
{hiddenPlugins.length > 0 && (
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', fontSize: '0.8rem', marginTop: '10px' }}>
<FaEyeSlash />
diff --git a/frontend/src/components/modals/MultiplePluginsInstallModal.tsx b/frontend/src/components/modals/MultiplePluginsInstallModal.tsx
index ba49ba92..9c86f3db 100644
--- a/frontend/src/components/modals/MultiplePluginsInstallModal.tsx
+++ b/frontend/src/components/modals/MultiplePluginsInstallModal.tsx
@@ -3,7 +3,7 @@ import { FC, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaCheck, FaDownload } from 'react-icons/fa';
-import { InstallType } from '../../plugin';
+import { InstallType, InstallTypeTranslationMapping } from '../../plugin';
interface MultiplePluginsInstallModalProps {
requests: { name: string; version: string; hash: string; install_type: InstallType }[];
@@ -12,13 +12,7 @@ interface MultiplePluginsInstallModalProps {
closeModal?(): void;
}
-// values are the JSON keys used in the translation file
-const InstallTypeTranslationMapping = {
- [InstallType.INSTALL]: 'install',
- [InstallType.REINSTALL]: 'reinstall',
- [InstallType.UPDATE]: 'update',
-} as const satisfies Record<InstallType, string>;
-
+// IMPORTANT! Keep in sync with `t(...)` comments below
type TitleTranslationMapping = 'mixed' | (typeof InstallTypeTranslationMapping)[InstallType];
const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
@@ -70,6 +64,8 @@ const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
if (requests.every(({ install_type }) => install_type === InstallType.INSTALL)) return 'install';
if (requests.every(({ install_type }) => install_type === InstallType.REINSTALL)) return 'reinstall';
if (requests.every(({ install_type }) => install_type === InstallType.UPDATE)) return 'update';
+ if (requests.every(({ install_type }) => install_type === InstallType.DOWNGRADE)) return 'downgrade';
+ if (requests.every(({ install_type }) => install_type === InstallType.OVERWRITE)) return 'overwrite';
return 'mixed';
}, [requests]);
@@ -86,14 +82,35 @@ const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
onCancel={async () => {
await onCancel();
}}
- strTitle={<div>{t(`MultiplePluginsInstallModal.title.${installTypeGrouped}`, { count: requests.length })}</div>}
- strOKButtonText={t(`MultiplePluginsInstallModal.ok_button.${loading ? 'loading' : 'idle'}`)}
+ strTitle={
+ <div>
+ {
+ // IMPORTANT! These comments are not cosmetic and are needed for `extracttext` task to work
+ // t('MultiplePluginsInstallModal.title.install', { count: n })
+ // t('MultiplePluginsInstallModal.title.reinstall', { count: n })
+ // t('MultiplePluginsInstallModal.title.update', { count: n })
+ // t('MultiplePluginsInstallModal.title.downgrade', { count: n })
+ // t('MultiplePluginsInstallModal.title.overwrite', { count: n })
+ // t('MultiplePluginsInstallModal.title.mixed', { count: n })
+ t(`MultiplePluginsInstallModal.title.${installTypeGrouped}`, { count: requests.length })
+ }
+ </div>
+ }
+ strOKButtonText={
+ loading ? t('MultiplePluginsInstallModal.ok_button.loading') : t('MultiplePluginsInstallModal.ok_button.idle')
+ }
>
<div>
{t('MultiplePluginsInstallModal.confirm')}
<ul style={{ listStyle: 'none', display: 'flex', flexDirection: 'column', gap: '4px' }}>
{requests.map(({ name, version, install_type, hash }, i) => {
const installTypeStr = InstallTypeTranslationMapping[install_type];
+ // IMPORTANT! These comments are not cosmetic and are needed for `extracttext` task to work
+ // t('MultiplePluginsInstallModal.description.install')
+ // t('MultiplePluginsInstallModal.description.reinstall')
+ // t('MultiplePluginsInstallModal.description.update')
+ // t('MultiplePluginsInstallModal.description.downgrade')
+ // t('MultiplePluginsInstallModal.description.overwrite')
const description = t(`MultiplePluginsInstallModal.description.${installTypeStr}`, {
name,
version,
diff --git a/frontend/src/components/modals/PluginInstallModal.tsx b/frontend/src/components/modals/PluginInstallModal.tsx
index 227bd818..16419d91 100644
--- a/frontend/src/components/modals/PluginInstallModal.tsx
+++ b/frontend/src/components/modals/PluginInstallModal.tsx
@@ -2,13 +2,13 @@ import { ConfirmModal, Navigation, ProgressBarWithInfo, QuickAccessTab } from '@
import { FC, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import TranslationHelper, { TranslationClass } from '../../utils/TranslationHelper';
+import { InstallType, InstallTypeTranslationMapping } from '../../plugin';
interface PluginInstallModalProps {
artifact: string;
version: string;
hash: string;
- installType: number;
+ installType: InstallType;
onOK(): void;
onCancel(): void;
closeModal?(): void;
@@ -44,6 +44,8 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
};
}, []);
+ const installTypeTranslationKey = InstallTypeTranslationMapping[installType];
+
return (
<ConfirmModal
bOKDisabled={loading}
@@ -59,12 +61,15 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
}}
strTitle={
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', width: '100%' }}>
- <TranslationHelper
- transClass={TranslationClass.PLUGIN_INSTALL_MODAL}
- transText="title"
- i18nArgs={{ artifact: artifact }}
- installType={installType}
- />
+ {
+ // IMPORTANT! These comments are not cosmetic and are needed for `extracttext` task to work
+ // t('PluginInstallModal.install.title')
+ // t('PluginInstallModal.reinstall.title')
+ // t('PluginInstallModal.update.title')
+ // t('PluginInstallModal.downgrade.title')
+ // t('PluginInstallModal.overwrite.title')
+ t(`PluginInstallModal.${installTypeTranslationKey}.title`, { artifact: artifact })
+ }
{loading && (
<div style={{ marginLeft: 'auto' }}>
<ProgressBarWithInfo
@@ -80,33 +85,44 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
strOKButtonText={
loading ? (
<div>
- <TranslationHelper
- transClass={TranslationClass.PLUGIN_INSTALL_MODAL}
- transText="button_processing"
- installType={installType}
- />
+ {
+ // IMPORTANT! These comments are not cosmetic and are needed for `extracttext` task to work
+ // t('PluginInstallModal.install.button_processing')
+ // t('PluginInstallModal.reinstall.button_processing')
+ // t('PluginInstallModal.update.button_processing')
+ // t('PluginInstallModal.downgrade.button_processing')
+ // t('PluginInstallModal.overwrite.button_processing')
+ t(`PluginInstallModal.${installTypeTranslationKey}.button_processing`)
+ }
</div>
) : (
<div>
- <TranslationHelper
- transClass={TranslationClass.PLUGIN_INSTALL_MODAL}
- transText="button_idle"
- installType={installType}
- />
+ {
+ // IMPORTANT! These comments are not cosmetic and are needed for `extracttext` task to work
+ // t('PluginInstallModal.install.button_idle')
+ // t('PluginInstallModal.reinstall.button_idle')
+ // t('PluginInstallModal.update.button_idle')
+ // t('PluginInstallModal.downgrade.button_idle')
+ // t('PluginInstallModal.overwrite.button_idle')
+ t(`PluginInstallModal.${installTypeTranslationKey}.button_idle`)
+ }
</div>
)
}
>
<div>
- <TranslationHelper
- transClass={TranslationClass.PLUGIN_INSTALL_MODAL}
- transText="desc"
- i18nArgs={{
+ {
+ // IMPORTANT! These comments are not cosmetic and are needed for `extracttext` task to work
+ // t('PluginInstallModal.install.desc')
+ // t('PluginInstallModal.reinstall.desc')
+ // t('PluginInstallModal.update.desc')
+ // t('PluginInstallModal.downgrade.desc')
+ // t('PluginInstallModal.overwrite.desc')
+ t(`PluginInstallModal.${installTypeTranslationKey}.desc`, {
artifact: artifact,
version: version,
- }}
- installType={installType}
- />
+ })
+ }
</div>
{hash == 'False' && <span style={{ color: 'red' }}>{t('PluginInstallModal.no_hash')}</span>}
</ConfirmModal>
diff --git a/frontend/src/components/store/PluginCard.tsx b/frontend/src/components/store/PluginCard.tsx
index 6e2a3510..f64abd09 100644
--- a/frontend/src/components/store/PluginCard.tsx
+++ b/frontend/src/components/store/PluginCard.tsx
@@ -1,18 +1,32 @@
import { ButtonItem, Dropdown, Focusable, PanelSectionRow, SingleDropdownOption, SuspensefulImage } from '@decky/ui';
import { CSSProperties, FC, useState } from 'react';
import { useTranslation } from 'react-i18next';
+import { FaArrowDown, FaArrowUp, FaCheck, FaDownload, FaRecycle } from 'react-icons/fa';
-import { InstallType } from '../../plugin';
-import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
+import { InstallType, Plugin } from '../../plugin';
+import { StorePlugin, requestPluginInstall } from '../../store';
import ExternalLink from '../ExternalLink';
interface PluginCardProps {
- plugin: StorePlugin;
+ storePlugin: StorePlugin;
+ installedPlugin: Plugin | undefined;
}
-const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
+const PluginCard: FC<PluginCardProps> = ({ storePlugin, installedPlugin }) => {
const [selectedOption, setSelectedOption] = useState<number>(0);
- const root = plugin.tags.some((tag) => tag === 'root');
+ const installedVersionIndex = storePlugin.versions.findIndex((version) => version.name === installedPlugin?.version);
+ const installType = // This assumes index in options is inverse to update order (i.e. newer updates are first)
+ installedPlugin && selectedOption < installedVersionIndex
+ ? InstallType.UPDATE
+ : installedPlugin && selectedOption === installedVersionIndex
+ ? InstallType.REINSTALL
+ : installedPlugin && selectedOption > installedVersionIndex
+ ? InstallType.DOWNGRADE
+ : installedPlugin // can happen if installed version is not in store
+ ? InstallType.OVERWRITE
+ : InstallType.INSTALL;
+
+ const root = storePlugin.tags.some((tag) => tag === 'root');
const { t } = useTranslation();
@@ -43,7 +57,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
height: '200px',
objectFit: 'cover',
}}
- src={plugin.image_url}
+ src={storePlugin.image_url}
/>
</div>
<div
@@ -69,7 +83,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
width: '90%',
}}
>
- {plugin.name}
+ {storePlugin.name}
</span>
<span
className="deckyStoreCardAuthor"
@@ -78,7 +92,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
fontSize: '1em',
}}
>
- {plugin.author}
+ {storePlugin.author}
</span>
<span
className="deckyStoreCardDescription"
@@ -91,8 +105,8 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
display: '-webkit-box',
}}
>
- {plugin.description ? (
- plugin.description
+ {storePlugin.description ? (
+ storePlugin.description
) : (
<span>
<i style={{ color: '#666' }}>{t('PluginCard.plugin_no_desc')}</i>
@@ -141,18 +155,49 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
bottomSeparator="none"
layout="below"
onClick={() =>
- requestPluginInstall(plugin.name, plugin.versions[selectedOption], InstallType.INSTALL)
+ requestPluginInstall(storePlugin.name, storePlugin.versions[selectedOption], installType)
}
>
- <span className="deckyStoreCardInstallText">{t('PluginCard.plugin_install')}</span>
+ <span
+ className="deckyStoreCardInstallText"
+ style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '5px' }}
+ >
+ {installType === InstallType.UPDATE ? (
+ <>
+ <FaArrowUp /> {t('PluginCard.plugin_update')}
+ </>
+ ) : installType === InstallType.REINSTALL ? (
+ <>
+ <FaRecycle /> {t('PluginCard.plugin_reinstall')}
+ </>
+ ) : installType === InstallType.DOWNGRADE ? (
+ <>
+ <FaArrowDown /> {t('PluginCard.plugin_downgrade')}
+ </>
+ ) : installType === InstallType.OVERWRITE ? (
+ <>
+ <FaDownload /> {t('PluginCard.plugin_overwrite')}
+ </>
+ ) : (
+ // installType === InstallType.INSTALL (also fallback)
+ <>
+ <FaDownload /> {t('PluginCard.plugin_install')}
+ </>
+ )}
+ </span>
</ButtonItem>
</div>
<div className="deckyStoreCardVersionContainer" style={{ minWidth: '130px' }}>
<Dropdown
rgOptions={
- plugin.versions.map((version: StorePluginVersion, index) => ({
+ storePlugin.versions.map((version, index) => ({
data: index,
- label: version.name,
+ label: (
+ <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
+ {version.name}
+ {installedPlugin && installedVersionIndex === index ? <FaCheck /> : null}
+ </div>
+ ),
})) as SingleDropdownOption[]
}
menuLabel={t('PluginCard.plugin_version_label') as string}
diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx
index 1094b243..3209ba08 100644
--- a/frontend/src/components/store/Store.tsx
+++ b/frontend/src/components/store/Store.tsx
@@ -14,6 +14,7 @@ import { useTranslation } from 'react-i18next';
import logo from '../../../assets/plugin_store.png';
import Logger from '../../logger';
import { SortDirections, SortOptions, Store, StorePlugin, getPluginList, getStore } from '../../store';
+import { useDeckyState } from '../DeckyState';
import ExternalLink from '../ExternalLink';
import PluginCard from './PluginCard';
@@ -104,6 +105,8 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
})();
}, []);
+ const { plugins: installedPlugins } = useDeckyState();
+
return (
<>
<style>{`
@@ -235,7 +238,12 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
plugin.tags.some((tag: string) => tag.toLowerCase().includes(searchFieldValue.toLowerCase()))
);
})
- .map((plugin: StorePlugin) => <PluginCard plugin={plugin} />)
+ .map((plugin: StorePlugin) => (
+ <PluginCard
+ storePlugin={plugin}
+ installedPlugin={installedPlugins.find((installedPlugin) => installedPlugin.name === plugin.name)}
+ />
+ ))
)}
</div>
</>
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index cb1bb270..88e85a48 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -146,9 +146,11 @@ class PluginLoader extends Logger {
});
this.routerHook.addRoute('/decky/store', () => (
- <WithSuspense route={true}>
- <StorePage />
- </WithSuspense>
+ <DeckyStateContextProvider deckyState={this.deckyState}>
+ <WithSuspense route={true}>
+ <StorePage />
+ </WithSuspense>
+ </DeckyStateContextProvider>
));
this.routerHook.addRoute('/decky/settings', () => {
return (
diff --git a/frontend/src/plugin.ts b/frontend/src/plugin.ts
index 0a6ae7aa..0035990e 100644
--- a/frontend/src/plugin.ts
+++ b/frontend/src/plugin.ts
@@ -18,8 +18,20 @@ export enum InstallType {
INSTALL,
REINSTALL,
UPDATE,
+ DOWNGRADE,
+ OVERWRITE,
}
+// values are the JSON keys used in the translation file
+// IMPORTANT! keep in sync with `t(...)` comments where this is used
+export const InstallTypeTranslationMapping = {
+ [InstallType.INSTALL]: 'install',
+ [InstallType.REINSTALL]: 'reinstall',
+ [InstallType.UPDATE]: 'update',
+ [InstallType.DOWNGRADE]: 'downgrade',
+ [InstallType.OVERWRITE]: 'overwrite',
+} as const satisfies Record<InstallType, string>;
+
type installPluginArgs = [
artifact: string,
name?: string,
diff --git a/frontend/src/utils/TranslationHelper.tsx b/frontend/src/utils/TranslationHelper.tsx
index 61bd24bf..4596060d 100644
--- a/frontend/src/utils/TranslationHelper.tsx
+++ b/frontend/src/utils/TranslationHelper.tsx
@@ -2,11 +2,9 @@ import { FC } from 'react';
import { Translation } from 'react-i18next';
import Logger from '../logger';
-import { InstallType } from '../plugin';
export enum TranslationClass {
PLUGIN_LOADER = 'PluginLoader',
- PLUGIN_INSTALL_MODAL = 'PluginInstallModal',
DEVELOPER = 'Developer',
}
@@ -19,7 +17,7 @@ interface TranslationHelperProps {
const logger = new Logger('TranslationHelper');
-const TranslationHelper: FC<TranslationHelperProps> = ({ transClass, transText, i18nArgs = null, installType = 0 }) => {
+const TranslationHelper: FC<TranslationHelperProps> = ({ transClass, transText, i18nArgs = null }) => {
return (
<Translation>
{(t, {}) => {
@@ -28,21 +26,6 @@ const TranslationHelper: FC<TranslationHelperProps> = ({ transClass, transText,
return i18nArgs
? t(TranslationClass.PLUGIN_LOADER + '.' + transText, i18nArgs)
: t(TranslationClass.PLUGIN_LOADER + '.' + transText);
- case TranslationClass.PLUGIN_INSTALL_MODAL:
- switch (installType) {
- case InstallType.INSTALL:
- return i18nArgs
- ? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.install.' + transText, i18nArgs)
- : t(TranslationClass.PLUGIN_INSTALL_MODAL + '.install.' + transText);
- case InstallType.REINSTALL:
- return i18nArgs
- ? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.reinstall.' + transText, i18nArgs)
- : t(TranslationClass.PLUGIN_INSTALL_MODAL + '.reinstall.' + transText);
- case InstallType.UPDATE:
- return i18nArgs
- ? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.update.' + transText, i18nArgs)
- : t(TranslationClass.PLUGIN_INSTALL_MODAL + '.update.' + transText);
- }
case TranslationClass.DEVELOPER:
return i18nArgs
? t(TranslationClass.DEVELOPER + '.' + transText, i18nArgs)