From 457e099425329eecad5193360fa934d72cc19463 Mon Sep 17 00:00:00 2001 From: Marco Rodolfi Date: Fri, 9 Aug 2024 09:47:12 +0200 Subject: [PATCH 1/2] Rebase warning messages on latest main --- backend/decky_loader/locales/en-US.json | 9 +++ .../src/components/modals/WarnThirdParty.tsx | 78 +++++++++++++++++++ .../settings/pages/developer/index.tsx | 22 +++++- .../settings/pages/general/StoreSelect.tsx | 44 +++++++---- frontend/src/utils/TranslationHelper.tsx | 24 +++++- frontend/src/utils/globalTypes.ts | 4 + 6 files changed, 164 insertions(+), 17 deletions(-) create mode 100644 frontend/src/components/modals/WarnThirdParty.tsx create mode 100644 frontend/src/utils/globalTypes.ts diff --git a/backend/decky_loader/locales/en-US.json b/backend/decky_loader/locales/en-US.json index 23566026b..b82686e1b 100644 --- a/backend/decky_loader/locales/en-US.json +++ b/backend/decky_loader/locales/en-US.json @@ -254,6 +254,15 @@ "testing": "Testing" } }, + "WarnThirdParty":{ + "title_zip": "Third-Party Plugin Installation", + "title_repo": "Third-Party Store Selection", + "button_processing_one": "Please wait {{timer}} second", + "button_processing_many": "Please wait {{timer}} seconds", + "button_idle": "Continue", + "desc_zip": "The Decky Loader team has not reviewed this plugin. It may contain malware, such as software to steal your Steam account or harm your device. By installing this plugin, you agree you have assumed all risks to your device.", + "desc_repo": "The Decky Loader team does not maintain this plugin store. It and its plugins may contain malware, such as software to steal your Steam account or harm your device. By adding this store, you agree you have assumed all risks to your device." + }, "Testing": { "download": "Download", "error": "Error Installing PR", diff --git a/frontend/src/components/modals/WarnThirdParty.tsx b/frontend/src/components/modals/WarnThirdParty.tsx new file mode 100644 index 000000000..a6a4c3f66 --- /dev/null +++ b/frontend/src/components/modals/WarnThirdParty.tsx @@ -0,0 +1,78 @@ +import { ConfirmModal } from '@decky/ui'; +import { FC, useEffect, useState } from 'react'; +import { FaExclamationTriangle } from 'react-icons/fa'; + +import TranslationHelper, { TranslationClass } from '../../utils/TranslationHelper'; +import { WarnThirdPartyType } from '../../utils/globalTypes'; + +interface WarnThirdPartyProps { + seconds?: number; + type: WarnThirdPartyType; + onOK(): void; + onCancel(): void; + closeModal?(): void; +} + +const WarnThirdParty: FC = ({ seconds = 5, type, onOK, onCancel, closeModal }) => { + const [waitTimer, setWaitTimer] = useState(seconds); + + useEffect(() => { + // exit early when we reach 0 + if (waitTimer <= 0) return; + + // save intervalId to clear the interval when the + // component re-renders + const intervalId = setInterval(() => { + setWaitTimer(waitTimer - 1); + }, 1000); + + // clear interval on re-render to avoid memory leaks + return () => clearInterval(intervalId); + // add waitTimer as a dependency to re-rerun the effect + // when we update it + }, [waitTimer]); + + return ( + 0} + closeModal={closeModal} + onOK={async () => { + await onOK(); + }} + onCancel={async () => { + await onCancel(); + }} + strTitle={ +
+ + +
+ } + strOKButtonText={ + waitTimer > 0 ? ( +
+ +
+ ) : ( +
+ +
+ ) + } + > + +
+ +
+
+
+ ); +}; + +export default WarnThirdParty; diff --git a/frontend/src/components/settings/pages/developer/index.tsx b/frontend/src/components/settings/pages/developer/index.tsx index 099f26101..f828b39e2 100644 --- a/frontend/src/components/settings/pages/developer/index.tsx +++ b/frontend/src/components/settings/pages/developer/index.tsx @@ -7,6 +7,7 @@ import { Navigation, TextField, Toggle, + showModal } from '@decky/ui'; import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -18,6 +19,8 @@ import { installFromURL } from '../../../../store'; import { useSetting } from '../../../../utils/hooks/useSetting'; import { getSetting } from '../../../../utils/settings'; import { FileSelectionType } from '../../../modals/filepicker'; +import WarnThirdParty from '../../../modals/WarnThirdParty'; +import { WarnThirdPartyType } from '../../../../utils/globalTypes'; import RemoteDebuggingSettings from '../general/RemoteDebugging'; const logger = new Logger('DeveloperIndex'); @@ -43,6 +46,8 @@ export default function DeveloperSettings() { const [enableValveInternal, setEnableValveInternal] = useSetting('developer.valve_internal', false); const [reactDevtoolsEnabled, setReactDevtoolsEnabled] = useSetting('developer.rdt.enabled', false); const [reactDevtoolsIP, setReactDevtoolsIP] = useSetting('developer.rdt.ip', ''); + const [acceptedWarning, setAcceptedWarning] = useSetting('developer.warn.third_party', false); + const waitTime = acceptedWarning ? 0 : 5; const [pluginURL, setPluginURL] = useState(''); const textRef = useRef(null); const { t } = useTranslation(); @@ -72,7 +77,22 @@ export default function DeveloperSettings() { } icon={} > - installFromURL(pluginURL)}> + + showModal( + { + setAcceptedWarning(true); + installFromURL(pluginURL); + }} + onCancel={() => {}} + seconds={waitTime} + />, + ) + } + > {t('SettingsDeveloperIndex.third_party_plugins.button_install')} diff --git a/frontend/src/components/settings/pages/general/StoreSelect.tsx b/frontend/src/components/settings/pages/general/StoreSelect.tsx index 9cc7d5c9d..7e92e76bf 100644 --- a/frontend/src/components/settings/pages/general/StoreSelect.tsx +++ b/frontend/src/components/settings/pages/general/StoreSelect.tsx @@ -1,4 +1,4 @@ -import { Dropdown, Field, TextField } from '@decky/ui'; +import { Dropdown, Field, TextField, showModal } from '@decky/ui'; import { FunctionComponent } from 'react'; import { useTranslation } from 'react-i18next'; import { FaShapes } from 'react-icons/fa'; @@ -6,12 +6,16 @@ import { FaShapes } from 'react-icons/fa'; import Logger from '../../../../logger'; import { Store } from '../../../../store'; import { useSetting } from '../../../../utils/hooks/useSetting'; +import WarnThirdParty from '../../../modals/WarnThirdParty'; +import { WarnThirdPartyType } from '../../../../utils/globalTypes'; const logger = new Logger('StoreSelect'); const StoreSelect: FunctionComponent<{}> = () => { const [selectedStore, setSelectedStore] = useSetting('store', Store.Default); const [selectedStoreURL, setSelectedStoreURL] = useSetting('store-url', null); + const [acceptedWarning, setAcceptedWarning] = useSetting('store_select.warn.third_party', false); + const waitTime = acceptedWarning ? 0 : 5; const { t } = useTranslation(); const tStores = [ t('StoreSelect.store_channel.default'), @@ -38,20 +42,30 @@ const StoreSelect: FunctionComponent<{}> = () => { }} /> - {selectedStore == Store.Custom && ( - setSelectedStoreURL(e?.target.value || null)} - /> - } - icon={} - > - )} + {selectedStore == Store.Custom && + showModal( + { + setAcceptedWarning(true); + }} + onCancel={() => setSelectedStore(Store.Default)} + />, + ) && ( + setSelectedStoreURL(e?.target.value || null)} + /> + } + icon={} + > + )} ); }; diff --git a/frontend/src/utils/TranslationHelper.tsx b/frontend/src/utils/TranslationHelper.tsx index 61bd24bfb..37f2f4889 100644 --- a/frontend/src/utils/TranslationHelper.tsx +++ b/frontend/src/utils/TranslationHelper.tsx @@ -1,6 +1,7 @@ import { FC } from 'react'; import { Translation } from 'react-i18next'; +import { WarnThirdPartyType } from './globalTypes'; import Logger from '../logger'; import { InstallType } from '../plugin'; @@ -8,6 +9,7 @@ export enum TranslationClass { PLUGIN_LOADER = 'PluginLoader', PLUGIN_INSTALL_MODAL = 'PluginInstallModal', DEVELOPER = 'Developer', + WARN_THIRD_PARTY = 'WarnThirdParty', } interface TranslationHelperProps { @@ -15,11 +17,12 @@ interface TranslationHelperProps { transText: string; i18nArgs?: {}; installType?: number; + warnType?: WarnThirdPartyType; } const logger = new Logger('TranslationHelper'); -const TranslationHelper: FC = ({ transClass, transText, i18nArgs = null, installType = 0 }) => { +const TranslationHelper: FC = ({ transClass, transText, i18nArgs = null, installType = 0, warnType = WarnThirdPartyType.REPO }) => { return ( {(t, {}) => { @@ -47,6 +50,25 @@ const TranslationHelper: FC = ({ transClass, transText, return i18nArgs ? t(TranslationClass.DEVELOPER + '.' + transText, i18nArgs) : t(TranslationClass.DEVELOPER + '.' + transText); + //Handle different messages in different class cases + case TranslationClass.WARN_THIRD_PARTY: + //Needed only for title and description + if (!transText.startsWith('button')) { + switch (warnType) { + case WarnThirdPartyType.REPO: + return i18nArgs + ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_repo', i18nArgs) + : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_repo'); + case WarnThirdPartyType.ZIP: + return i18nArgs + ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_zip', i18nArgs) + : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_zip'); + } + } else { + return i18nArgs + ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText, i18nArgs) + : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText); + } default: logger.error('We should never fall in the default case!'); return ''; diff --git a/frontend/src/utils/globalTypes.ts b/frontend/src/utils/globalTypes.ts new file mode 100644 index 000000000..8f1a1fa09 --- /dev/null +++ b/frontend/src/utils/globalTypes.ts @@ -0,0 +1,4 @@ +export enum WarnThirdPartyType { + REPO = 0, + ZIP = 1, + } \ No newline at end of file From 0f13ad09fefdef8fea0818eb39c1ca6d06a07642 Mon Sep 17 00:00:00 2001 From: Marco Rodolfi Date: Fri, 9 Aug 2024 09:49:54 +0200 Subject: [PATCH 2/2] Linting cleanup --- .../src/components/modals/WarnThirdParty.tsx | 2 +- .../settings/pages/developer/index.tsx | 4 +- .../settings/pages/general/StoreSelect.tsx | 2 +- frontend/src/utils/TranslationHelper.tsx | 42 +++++++++++-------- frontend/src/utils/globalTypes.ts | 6 +-- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/modals/WarnThirdParty.tsx b/frontend/src/components/modals/WarnThirdParty.tsx index a6a4c3f66..e7ff45617 100644 --- a/frontend/src/components/modals/WarnThirdParty.tsx +++ b/frontend/src/components/modals/WarnThirdParty.tsx @@ -2,8 +2,8 @@ import { ConfirmModal } from '@decky/ui'; import { FC, useEffect, useState } from 'react'; import { FaExclamationTriangle } from 'react-icons/fa'; -import TranslationHelper, { TranslationClass } from '../../utils/TranslationHelper'; import { WarnThirdPartyType } from '../../utils/globalTypes'; +import TranslationHelper, { TranslationClass } from '../../utils/TranslationHelper'; interface WarnThirdPartyProps { seconds?: number; diff --git a/frontend/src/components/settings/pages/developer/index.tsx b/frontend/src/components/settings/pages/developer/index.tsx index f828b39e2..e5f355468 100644 --- a/frontend/src/components/settings/pages/developer/index.tsx +++ b/frontend/src/components/settings/pages/developer/index.tsx @@ -7,7 +7,7 @@ import { Navigation, TextField, Toggle, - showModal + showModal, } from '@decky/ui'; import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -16,11 +16,11 @@ import { FaFileArchive, FaLink, FaReact, FaSteamSymbol, FaTerminal } from 'react import { setShouldConnectToReactDevTools, setShowValveInternal } from '../../../../developer'; import Logger from '../../../../logger'; import { installFromURL } from '../../../../store'; +import { WarnThirdPartyType } from '../../../../utils/globalTypes'; import { useSetting } from '../../../../utils/hooks/useSetting'; import { getSetting } from '../../../../utils/settings'; import { FileSelectionType } from '../../../modals/filepicker'; import WarnThirdParty from '../../../modals/WarnThirdParty'; -import { WarnThirdPartyType } from '../../../../utils/globalTypes'; import RemoteDebuggingSettings from '../general/RemoteDebugging'; const logger = new Logger('DeveloperIndex'); diff --git a/frontend/src/components/settings/pages/general/StoreSelect.tsx b/frontend/src/components/settings/pages/general/StoreSelect.tsx index 7e92e76bf..80bc4930c 100644 --- a/frontend/src/components/settings/pages/general/StoreSelect.tsx +++ b/frontend/src/components/settings/pages/general/StoreSelect.tsx @@ -5,9 +5,9 @@ import { FaShapes } from 'react-icons/fa'; import Logger from '../../../../logger'; import { Store } from '../../../../store'; +import { WarnThirdPartyType } from '../../../../utils/globalTypes'; import { useSetting } from '../../../../utils/hooks/useSetting'; import WarnThirdParty from '../../../modals/WarnThirdParty'; -import { WarnThirdPartyType } from '../../../../utils/globalTypes'; const logger = new Logger('StoreSelect'); diff --git a/frontend/src/utils/TranslationHelper.tsx b/frontend/src/utils/TranslationHelper.tsx index 37f2f4889..c499f39da 100644 --- a/frontend/src/utils/TranslationHelper.tsx +++ b/frontend/src/utils/TranslationHelper.tsx @@ -1,9 +1,9 @@ import { FC } from 'react'; import { Translation } from 'react-i18next'; -import { WarnThirdPartyType } from './globalTypes'; import Logger from '../logger'; import { InstallType } from '../plugin'; +import { WarnThirdPartyType } from './globalTypes'; export enum TranslationClass { PLUGIN_LOADER = 'PluginLoader', @@ -22,7 +22,13 @@ interface TranslationHelperProps { const logger = new Logger('TranslationHelper'); -const TranslationHelper: FC = ({ transClass, transText, i18nArgs = null, installType = 0, warnType = WarnThirdPartyType.REPO }) => { +const TranslationHelper: FC = ({ + transClass, + transText, + i18nArgs = null, + installType = 0, + warnType = WarnThirdPartyType.REPO, +}) => { return ( {(t, {}) => { @@ -50,25 +56,25 @@ const TranslationHelper: FC = ({ transClass, transText, return i18nArgs ? t(TranslationClass.DEVELOPER + '.' + transText, i18nArgs) : t(TranslationClass.DEVELOPER + '.' + transText); - //Handle different messages in different class cases - case TranslationClass.WARN_THIRD_PARTY: - //Needed only for title and description - if (!transText.startsWith('button')) { + //Handle different messages in different class cases + case TranslationClass.WARN_THIRD_PARTY: + //Needed only for title and description + if (!transText.startsWith('button')) { switch (warnType) { - case WarnThirdPartyType.REPO: - return i18nArgs - ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_repo', i18nArgs) - : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_repo'); - case WarnThirdPartyType.ZIP: - return i18nArgs - ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_zip', i18nArgs) - : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_zip'); + case WarnThirdPartyType.REPO: + return i18nArgs + ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_repo', i18nArgs) + : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_repo'); + case WarnThirdPartyType.ZIP: + return i18nArgs + ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_zip', i18nArgs) + : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText + '_zip'); } - } else { + } else { return i18nArgs - ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText, i18nArgs) - : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText); - } + ? t(TranslationClass.WARN_THIRD_PARTY + '.' + transText, i18nArgs) + : t(TranslationClass.WARN_THIRD_PARTY + '.' + transText); + } default: logger.error('We should never fall in the default case!'); return ''; diff --git a/frontend/src/utils/globalTypes.ts b/frontend/src/utils/globalTypes.ts index 8f1a1fa09..26f7d1aec 100644 --- a/frontend/src/utils/globalTypes.ts +++ b/frontend/src/utils/globalTypes.ts @@ -1,4 +1,4 @@ export enum WarnThirdPartyType { - REPO = 0, - ZIP = 1, - } \ No newline at end of file + REPO = 0, + ZIP = 1, +}