summaryrefslogtreecommitdiff
path: root/frontend/src/components/modals
diff options
context:
space:
mode:
authorÁlvaro Cuesta <1827495+alvaro-cuesta@users.noreply.github.com>2025-01-02 20:38:40 +0100
committerGitHub <noreply@github.com>2025-01-02 11:38:40 -0800
commitf6144f9634482d6bd0ed88a31495c6c6c88add96 (patch)
tree21797e504d62c33c1514870a457e36d3b4f3cbac /frontend/src/components/modals
parent79bb62a3c4f69dbd57dc037da1269285cae1b26d (diff)
downloaddecky-loader-75079fafd9b64a4fd1479fbb7726c3fcded3c5fb.tar.gz
decky-loader-75079fafd9b64a4fd1479fbb7726c3fcded3c5fb.zip
feat: sync with local plugin status in store (#733)v3.1.0-pre1
* fix: useDeckyState proper type and safety * refactor: plugin list Avoids unneeded re-renders. See https://react.dev/learn/you-might-not-need-an-effect#caching-expensive-calculations * feat: sync with local plugin status in store Adds some QoL changes to the plugin store browser: - Add ✓ icon to currently installed plugin version in version selector - Change install button label depending on the install type that the button would trigger - Adds icon to install button for clarity The goal is to make it clear to the user what the current state of the installed plugin is, and what would be the impact of installing the selected version. Resolves #360 * lint: prettier * fix: add missing translations * refactor: safer translation strings on install Prefer using `t(...)` instead of `TranslationHelper` since it ensures that the translation keys are not missing in the locale files when running the `extractext` task. By adding comments with `t(...)` calls, `i18next-parser` will generate the strings as if they were present as literals in the code (see https://github.com/i18next/i18next-parser#caveats). This does _not_ suppress the warnings (since `i18next-parser` does not have access to TS types, so it cannot infer template literals) but it at least makes it less likely that a translation will be missed by mistake, have typos, etc.
Diffstat (limited to 'frontend/src/components/modals')
-rw-r--r--frontend/src/components/modals/MultiplePluginsInstallModal.tsx37
-rw-r--r--frontend/src/components/modals/PluginInstallModal.tsx66
2 files changed, 68 insertions, 35 deletions
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>