summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorMarco Rodolfi <marco.rodolfi@tuta.io>2023-05-27 13:55:26 +0200
committerMarco Rodolfi <marco.rodolfi@tuta.io>2023-05-27 13:55:26 +0200
commit6e6f8caca807979be4889a5c1292aa15309a6a3a (patch)
treedb240b8b4851045308b26ebf7d7382625ecf2ff6 /frontend
parent3a83062438e2e86dbaaf459f8af7a2fd5a8df215 (diff)
downloaddecky-loader-6e6f8caca807979be4889a5c1292aa15309a6a3a.tar.gz
decky-loader-6e6f8caca807979be4889a5c1292aa15309a6a3a.zip
Unified translation classes, fixed missing toaster translation and improved the error styling report.
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/modals/PluginInstallModal.tsx46
-rw-r--r--frontend/src/components/modals/TPluginInstallModal.tsx95
-rw-r--r--frontend/src/plugin-loader.tsx95
-rw-r--r--frontend/src/utils/TranslationHelper.tsx59
4 files changed, 157 insertions, 138 deletions
diff --git a/frontend/src/components/modals/PluginInstallModal.tsx b/frontend/src/components/modals/PluginInstallModal.tsx
index 0e8e3d47..b37dbc65 100644
--- a/frontend/src/components/modals/PluginInstallModal.tsx
+++ b/frontend/src/components/modals/PluginInstallModal.tsx
@@ -2,7 +2,7 @@ import { ConfirmModal, Navigation, QuickAccessTab } from 'decky-frontend-lib';
import { FC, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import TPluginInstallModal, { TranslatedPart } from './TPluginInstallModal';
+import TranslationHelper, { TranslationClass } from '../../utils/TranslationHelper';
interface PluginInstallModalProps {
artifact: string;
@@ -39,21 +39,47 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
onCancel={async () => {
await onCancel();
}}
- strTitle={<TPluginInstallModal trans_part={TranslatedPart.TITLE} trans_type={installType} artifact={artifact} />}
+ strTitle={
+ <div>
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
+ trans_text="title"
+ i18n_args={{ artifact: artifact }}
+ install_type={installType}
+ />
+ </div>
+ }
strOKButtonText={
loading ? (
- <TPluginInstallModal trans_part={TranslatedPart.BUTTON_PROC} trans_type={installType} />
+ <div>
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
+ trans_text="button_processing"
+ install_type={installType}
+ />
+ </div>
) : (
- <TPluginInstallModal trans_part={TranslatedPart.BUTTON_IDLE} trans_type={installType} />
+ <div>
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
+ trans_text="button_idle"
+ install_type={installType}
+ />
+ </div>
)
}
>
- <TPluginInstallModal
- trans_part={TranslatedPart.DESC}
- trans_type={installType}
- artifact={artifact}
- version={version ? version : ''}
- />
+ <div>
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_INSTALL_MODAL}
+ trans_text="desc"
+ i18n_args={{
+ artifact: artifact,
+ version: version,
+ }}
+ install_type={installType}
+ />
+ </div>
{hash == 'False' && <span style={{ color: 'red' }}>{t('PluginInstallModal.no_hash')}</span>}
</ConfirmModal>
);
diff --git a/frontend/src/components/modals/TPluginInstallModal.tsx b/frontend/src/components/modals/TPluginInstallModal.tsx
deleted file mode 100644
index 3866560e..00000000
--- a/frontend/src/components/modals/TPluginInstallModal.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { FC } from 'react';
-import { Translation } from 'react-i18next';
-
-import { InstallType } from '../../plugin';
-
-export enum TranslatedPart {
- TITLE,
- DESC,
- BUTTON_IDLE,
- BUTTON_PROC,
-}
-interface TPluginInstallModalProps {
- trans_part: TranslatedPart;
- trans_type: number;
- artifact?: string;
- version?: string;
-}
-
-const TPluginInstallModal: FC<TPluginInstallModalProps> = ({ trans_part, trans_type, artifact, version }) => {
- return (
- <Translation>
- {(t, {}) => {
- switch (trans_part) {
- case TranslatedPart.TITLE:
- switch (trans_type) {
- case InstallType.INSTALL:
- return <div>{t('PluginInstallModal.install.title', { artifact: artifact })}</div>;
- case InstallType.REINSTALL:
- return <div>{t('PluginInstallModal.reinstall.title', { artifact: artifact })}</div>;
- case InstallType.UPDATE:
- return <div>{t('PluginInstallModal.update.title', { artifact: artifact })}</div>;
- default:
- return null;
- }
- case TranslatedPart.DESC:
- switch (trans_type) {
- case InstallType.INSTALL:
- return (
- <div>
- {t('PluginInstallModal.install.desc', {
- artifact: artifact,
- version: version,
- })}
- </div>
- );
- case InstallType.REINSTALL:
- return (
- <div>
- {t('PluginInstallModal.reinstall.desc', {
- artifact: artifact,
- version: version,
- })}
- </div>
- );
- case InstallType.UPDATE:
- return (
- <div>
- {t('PluginInstallModal.update.desc', {
- artifact: artifact,
- version: version,
- })}
- </div>
- );
- default:
- return null;
- }
- case TranslatedPart.BUTTON_IDLE:
- switch (trans_type) {
- case InstallType.INSTALL:
- return <div>{t('PluginInstallModal.install.button_idle')}</div>;
- case InstallType.REINSTALL:
- return <div>{t('PluginInstallModal.reinstall.button_idle')}</div>;
- case InstallType.UPDATE:
- return <div>{t('PluginInstallModal.update.button_idle')}</div>;
- default:
- return null;
- }
- case TranslatedPart.BUTTON_PROC:
- switch (trans_type) {
- case InstallType.INSTALL:
- return <div>{t('PluginInstallModal.install.button_processing')}</div>;
- case InstallType.REINSTALL:
- return <div>{t('PluginInstallModal.reinstall.button_processing')}</div>;
- case InstallType.UPDATE:
- return <div>{t('PluginInstallModal.update.button_processing')}</div>;
- default:
- return null;
- }
- }
- }}
- </Translation>
- );
-};
-
-export default TPluginInstallModal;
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index 71c8d0df..7a226920 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -1,8 +1,19 @@
-import { ConfirmModal, ModalRoot, Patch, QuickAccessTab, Router, showModal, sleep } from 'decky-frontend-lib';
-import { FC, lazy } from 'react';
-import { Trans, Translation } from 'react-i18next';
-import { IconContext } from 'react-icons';
-import { FaCog, FaExclamationCircle, FaPlug } from 'react-icons/fa';
+import {
+ ConfirmModal,
+ ModalRoot,
+ PanelSection,
+ PanelSectionRow,
+ Patch,
+ QuickAccessTab,
+ Router,
+ showModal,
+ sleep,
+ staticClasses,
+} from 'decky-frontend-lib';
+import { CSSProperties, FC, lazy } from 'react';
+import { Trans } from 'react-i18next';
+import { BsGearFill } from 'react-icons/bs';
+import { FaArrowRight, FaExclamationCircle, FaPlug } from 'react-icons/fa';
import { DeckyState, DeckyStateContextProvider, useDeckyState } from './components/DeckyState';
import LegacyPlugin from './components/LegacyPlugin';
@@ -21,6 +32,7 @@ import OldTabsHook from './tabs-hook.old';
import Toaster from './toaster';
import { VerInfo, callUpdaterMethod } from './updater';
import { getSetting } from './utils/settings';
+import TranslationHelper, { TranslationClass } from './utils/TranslationHelper';
const StorePage = lazy(() => import('./components/store/Store'));
const SettingsPage = lazy(() => import('./components/settings'));
@@ -100,10 +112,14 @@ class PluginLoader extends Logger {
const versionInfo = await this.updateVersion();
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
this.toaster.toast({
- //title: t('PluginLoader.decky_title'),
- title: 'Decky',
- //body: t('PluginLoader.decky_update_available', { tag_name: versionInfo?.remote?.tag_name }),
- body: `Update to ${versionInfo?.remote?.tag_name} available!`,
+ title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />,
+ body: (
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_LOADER}
+ trans_text="decky_update_available"
+ i18n_args={{ tag_name: versionInfo?.remote?.tag_name }}
+ />
+ ),
onClick: () => Router.Navigate('/decky/settings'),
});
this.deckyState.setHasLoaderUpdate(true);
@@ -122,10 +138,14 @@ class PluginLoader extends Logger {
const updates = await this.checkPluginUpdates();
if (updates?.size > 0) {
this.toaster.toast({
- //title: t('PluginLoader.decky_title'),
- title: 'Decky',
- //body: t('PluginLoader.plugin_update', { count: updates.size }),
- body: `Updates available for ${updates.size} plugin${updates.size > 1 ? 's' : ''}!`,
+ title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />,
+ body: (
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_LOADER}
+ trans_text="plugin_update"
+ i18n_args={{ count: updates.size }}
+ />
+ ),
onClick: () => Router.Navigate('/decky/settings/plugins'),
});
}
@@ -256,25 +276,29 @@ class PluginLoader extends Logger {
});
} catch (e) {
this.error('Error loading plugin ' + name, e);
+ const style: CSSProperties = { verticalAlign: 'middle' };
const TheError: FC<{}> = () => (
- <Translation>
- {(t, {}) => {
- return (
- <>
- {t('PluginLoader.error')}:{' '}
- <pre>
- <code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
- </pre>
- <div>
- <Trans
- i18nKey="PluginLoader.plugin_error_uninstall"
- components={[<FaCog style={{ verticalAlign: 'middle' }} />]}
- />
- </div>
- </>
- );
- }}
- </Translation>
+ <PanelSection>
+ <PanelSectionRow>
+ <div className={staticClasses.FriendsTitle} style={{ display: 'flex', justifyContent: 'center' }}>
+ {<TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="error" />}
+ </div>
+ </PanelSectionRow>
+ <PanelSectionRow>
+ <pre style={{ overflowX: 'scroll' }}>
+ <code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
+ </pre>
+ </PanelSectionRow>
+ <PanelSectionRow>
+ <div className={staticClasses.Text}>
+ <Trans
+ i18nKey="PluginLoader.plugin_error_uninstall"
+ values={{ name: name }}
+ components={[<BsGearFill style={style} />, <FaArrowRight style={style} />, <FaPlug style={style} />]}
+ />
+ </div>
+ </PanelSectionRow>
+ </PanelSection>
);
this.plugins.push({
name: name,
@@ -283,8 +307,13 @@ class PluginLoader extends Logger {
icon: <FaExclamationCircle />,
});
this.toaster.toast({
- //title: t('PluginLoader.plugin_load_error.toast', { name: name }),
- title: 'Error loading ' + name,
+ title: (
+ <TranslationHelper
+ trans_class={TranslationClass.PLUGIN_LOADER}
+ trans_text="plugin_load_error.toast"
+ i18n_args={{ name: name }}
+ />
+ ),
body: '' + e,
icon: <FaExclamationCircle />,
});
diff --git a/frontend/src/utils/TranslationHelper.tsx b/frontend/src/utils/TranslationHelper.tsx
new file mode 100644
index 00000000..457a3159
--- /dev/null
+++ b/frontend/src/utils/TranslationHelper.tsx
@@ -0,0 +1,59 @@
+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',
+}
+
+interface TranslationHelperProps {
+ trans_class: TranslationClass;
+ trans_text: string;
+ i18n_args?: {};
+ install_type?: number;
+}
+
+const logger = new Logger('TranslationHelper');
+
+const TranslationHelper: FC<TranslationHelperProps> = ({
+ trans_class,
+ trans_text,
+ i18n_args = null,
+ install_type = 0,
+}) => {
+ return (
+ <Translation>
+ {(t, {}) => {
+ switch (trans_class) {
+ case TranslationClass.PLUGIN_LOADER:
+ return i18n_args
+ ? t(TranslationClass.PLUGIN_LOADER + '.' + trans_text, i18n_args)
+ : t(TranslationClass.PLUGIN_LOADER + '.' + trans_text);
+ case TranslationClass.PLUGIN_INSTALL_MODAL:
+ switch (install_type) {
+ case InstallType.INSTALL:
+ return i18n_args
+ ? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.install.' + trans_text, i18n_args)
+ : t(TranslationClass.PLUGIN_INSTALL_MODAL + '.install.' + trans_text);
+ case InstallType.REINSTALL:
+ return i18n_args
+ ? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.reinstall.' + trans_text, i18n_args)
+ : t(TranslationClass.PLUGIN_INSTALL_MODAL + '.reinstall.' + trans_text);
+ case InstallType.UPDATE:
+ return i18n_args
+ ? t(TranslationClass.PLUGIN_INSTALL_MODAL + '.update.' + trans_text, i18n_args)
+ : t(TranslationClass.PLUGIN_INSTALL_MODAL + '.update.' + trans_text);
+ }
+ default:
+ logger.error('We should never fall in the default case!');
+ return '';
+ }
+ }}
+ </Translation>
+ );
+};
+
+export default TranslationHelper;