Refactor languages and fix hooks logic bugs.

This commit is contained in:
Marco Rodolfi
2023-01-31 15:27:28 +01:00
parent eaf7239dd1
commit 25e3f84ddd
45 changed files with 777 additions and 335 deletions
@@ -2,8 +2,6 @@ import { ConfirmModal, Navigation, QuickAccessTab } from 'decky-frontend-lib';
import { FC, useState } from 'react';
import { useTranslation } from 'react-i18next';
const { t } = useTranslation('PluginInstallModal');
interface PluginInstallModalProps {
artifact: string;
version: string;
@@ -16,6 +14,7 @@ interface PluginInstallModalProps {
const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, hash, onOK, onCancel, closeModal }) => {
const [loading, setLoading] = useState<boolean>(false);
const { t } = useTranslation();
return (
<ConfirmModal
bOKDisabled={loading}
@@ -29,13 +28,15 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
onCancel={async () => {
await onCancel();
}}
strTitle={t('install.title', artifact)}
strOKButtonText={loading ? t('install.button_processing') : t('install.button_idle')}
strTitle={t('PluginInstallModal.install.title', artifact)}
strOKButtonText={
loading ? t('PluginInstallModal.install.button_processing') : t('PluginInstallModal.install.button_idle')
}
>
{hash == 'False' ? (
<h3 style={{ color: 'red' }}>!!!!NO HASH PROVIDED!!!!</h3>
) : (
t('install.desc', artifact, version)
t('PluginInstallModal.install.desc', artifact, version)
)}
</ConfirmModal>
);
+5 -6
View File
@@ -9,19 +9,18 @@ import PluginList from './pages/plugin_list';
const DeveloperSettings = lazy(() => import('./pages/developer'));
const { t } = useTranslation('SettingsIndex');
export default function SettingsPage() {
const [isDeveloper, setIsDeveloper] = useSetting<boolean>('developer.enabled', false);
const { t } = useTranslation();
const pages = [
{
title: t('general_title'),
title: t('SettingsIndex.general_title'),
content: <GeneralSettings isDeveloper={isDeveloper} setIsDeveloper={setIsDeveloper} />,
route: '/decky/settings/general',
},
{
title: t('plugins_title'),
title: t('SettingsIndex.plugins_title'),
content: <PluginList />,
route: '/decky/settings/plugins',
},
@@ -29,7 +28,7 @@ export default function SettingsPage() {
if (isDeveloper)
pages.push({
title: t('developer_title'),
title: t('SettingsIndex.developer_title'),
content: (
<WithSuspense>
<DeveloperSettings />
@@ -38,5 +37,5 @@ export default function SettingsPage() {
route: '/decky/settings/developer',
});
return <SidebarNavigation title={t('settings_navbar') as string} showTitle pages={pages} />;
return <SidebarNavigation title={t('SettingsIndex.settings_navbar') as string} showTitle pages={pages} />;
}
@@ -7,7 +7,6 @@ import { callUpdaterMethod } from '../../../../updater';
import { useSetting } from '../../../../utils/hooks/useSetting';
const logger = new Logger('BranchSelect');
const { t } = useTranslation('BranchSelect');
enum UpdateBranch {
Stable,
@@ -16,12 +15,13 @@ enum UpdateBranch {
}
const BranchSelect: FunctionComponent<{}> = () => {
const { t } = useTranslation();
const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Prerelease);
return (
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
// 0 being stable, 1 being pre-release and 2 being nightly
<Field label={t('update_channel.label')}>
<Field label={t('BranchSelect.update_channel.label')}>
<Dropdown
rgOptions={Object.values(UpdateBranch)
.filter((branch) => typeof branch == 'string')
@@ -6,12 +6,12 @@ import { useSetting } from '../../../../utils/hooks/useSetting';
export default function RemoteDebuggingSettings() {
const [allowRemoteDebugging, setAllowRemoteDebugging] = useSetting<boolean>('cef_forward', false);
const { t } = useTranslation('RemoteDebugging');
const { t } = useTranslation();
return (
<Field
label={t('remote_cef.label')}
description={<span style={{ whiteSpace: 'pre-line' }}>{t('remote_cef.desc')}</span>}
label={t('RemoteDebugging.remote_cef.label')}
description={<span style={{ whiteSpace: 'pre-line' }}>{t('RemoteDebugging.remote_cef.desc')}</span>}
icon={<FaBug style={{ display: 'block' }} />}
>
<Toggle
@@ -8,17 +8,17 @@ import { Store } from '../../../../store';
import { useSetting } from '../../../../utils/hooks/useSetting';
const logger = new Logger('StoreSelect');
const { t } = useTranslation('StoreSelect');
const StoreSelect: FunctionComponent<{}> = () => {
const [selectedStore, setSelectedStore] = useSetting<Store>('store', Store.Default);
const [selectedStoreURL, setSelectedStoreURL] = useSetting<string | null>('store-url', null);
const { t } = useTranslation();
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
// 0 being Default, 1 being Testing and 2 being Custom
return (
<>
<Field label={t('store_channel_label')}>
<Field label={t('StoreSelect.store_channel_label')}>
<Dropdown
rgOptions={Object.values(Store)
.filter((store) => typeof store == 'string')
@@ -35,7 +35,7 @@ const StoreSelect: FunctionComponent<{}> = () => {
</Field>
{selectedStore == Store.Custom && (
<Field
label={t('custom_store_label')}
label={t('StoreSelect.custom_store_label')}
indentLevel={1}
description={
<TextField
@@ -21,10 +21,10 @@ import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
import WithSuspense from '../../../WithSuspense';
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
const { t } = useTranslation('Updater');
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
const SP = findSP();
const { t } = useTranslation();
return (
<Focusable onCancelButton={closeModal}>
<FocusRing>
@@ -47,7 +47,7 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
<MarkdownRenderer onDismiss={closeModal}>{versionInfo.all[id].body}</MarkdownRenderer>
</WithSuspense>
) : (
t('no_patch_notes_desc')
t('Updater.no_patch_notes_desc')
)}
</div>
</Focusable>
@@ -60,7 +60,7 @@ function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | n
initialColumn={0}
autoFocus={true}
fnGetColumnWidth={() => SP.innerWidth}
name={t('decky_updates') as string}
name={t('Updater.decky_updates') as string}
/>
</FocusRing>
</Focusable>
@@ -74,6 +74,8 @@ export default function UpdaterSettings() {
const [updateProgress, setUpdateProgress] = useState<number>(-1);
const [reloading, setReloading] = useState<boolean>(false);
const { t } = useTranslation();
useEffect(() => {
window.DeckyUpdater = {
updateProgress: (i) => {
@@ -97,11 +99,13 @@ export default function UpdaterSettings() {
<Field
onOptionsActionDescription={versionInfo?.all ? 'Patch Notes' : undefined}
onOptionsButton={versionInfo?.all ? showPatchNotes : undefined}
label={t('updates.label')}
label={t('Updater.updates.label')}
description={
versionInfo && (
<span style={{ whiteSpace: 'pre-line' }}>{`${t('updates.cur_version', { ver: versionInfo.current })}\n${
versionInfo.updatable ? t('updates.lat_version', { ver: versionInfo.remote?.tag_name }) : ''
<span style={{ whiteSpace: 'pre-line' }}>{`${t('Updater.updates.cur_version', {
ver: versionInfo.current,
})}\n${
versionInfo.updatable ? t('Updater.updates.lat_version', { ver: versionInfo.remote?.tag_name }) : ''
}`}</span>
)
}
@@ -131,10 +135,10 @@ export default function UpdaterSettings() {
}
>
{checkingForUpdates
? t('updates.checking')
? t('Updater.updates.checking')
: !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
? t('updates.check_button')
: t('updates.install_button')}
? t('Updater.updates.check_button')
: t('Updater.updates.install_button')}
</DialogButton>
) : (
<ProgressBarWithInfo
@@ -142,7 +146,7 @@ export default function UpdaterSettings() {
bottomSeparator="none"
nProgress={updateProgress}
indeterminate={reloading}
sOperationText={reloading ? t('updates.reloading') : t('updates.updating')}
sOperationText={reloading ? t('Updater.updates.reloading') : t('Updater.updates.updating')}
/>
)}
</Field>
@@ -17,7 +17,7 @@ export default function GeneralSettings({
setIsDeveloper: (val: boolean) => void;
}) {
const [pluginURL, setPluginURL] = useState('');
const { t } = useTranslation('SettingsGeneralIndex');
const { t } = useTranslation();
return (
<div>
@@ -26,8 +26,8 @@ export default function GeneralSettings({
<StoreSelect />
<RemoteDebuggingSettings />
<Field
label={t('developer_mode.label')}
description={<span style={{ whiteSpace: 'pre-line' }}>{t('developer_mode.desc')}</span>}
label={t('SettingsGeneralIndex.developer_mode.label')}
description={<span style={{ whiteSpace: 'pre-line' }}>{t('SettingsGeneralIndex.developer_mode.desc')}</span>}
icon={<FaTools style={{ display: 'block' }} />}
>
<Toggle
@@ -38,12 +38,12 @@ export default function GeneralSettings({
/>
</Field>
<Field
label={t('manual_plugin.label')}
label={t('SettingsGeneralIndex.manual_plugin.label')}
description={<TextField label={'URL'} value={pluginURL} onChange={(e) => setPluginURL(e?.target.value)} />}
icon={<FaShapes style={{ display: 'block' }} />}
>
<DialogButton disabled={pluginURL.length == 0} onClick={() => installFromURL(pluginURL)}>
{t('manual_plugin.button')}
{t('SettingsGeneralIndex.manual_plugin.button')}
</DialogButton>
</Field>
</div>
@@ -8,7 +8,7 @@ import { useDeckyState } from '../../../DeckyState';
export default function PluginList() {
const { plugins, updates } = useDeckyState();
const { t } = useTranslation('PluginListIndex');
const { t } = useTranslation();
useEffect(() => {
window.DeckyPluginLoader.checkPluginUpdates();
@@ -17,7 +17,7 @@ export default function PluginList() {
if (plugins.length === 0) {
return (
<div>
<p>{t('list_no_plugin')}</p>
<p>{t('PluginListIndex.list_no_plugin')}</p>
</div>
);
}
@@ -38,7 +38,7 @@ export default function PluginList() {
onClick={() => requestPluginInstall(name, update)}
>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{t('list_update_to', update.name)}
{t('PluginListIndex.list_update_to', update.name)}
<FaDownload style={{ paddingLeft: '2rem' }} />
</div>
</DialogButton>
@@ -47,12 +47,12 @@ export default function PluginList() {
style={{ height: '40px', width: '40px', padding: '10px 12px', minWidth: '40px' }}
onClick={(e: MouseEvent) =>
showContextMenu(
<Menu label={t('list_plug_actions_label')}>
<Menu label={t('PluginListIndex.list_plug_actions_label')}>
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(name, version)}>
{t('reload')}
{t('PluginListIndex.reload')}
</MenuItem>
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(name)}>
{t('uninstall')}
{t('PluginListIndex.uninstall')}
</MenuItem>
</Menu>,
e.currentTarget ?? window,
+6 -6
View File
@@ -15,12 +15,12 @@ interface PluginCardProps {
plugin: StorePlugin;
}
const { t } = useTranslation('PluginCard');
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
const [selectedOption, setSelectedOption] = useState<number>(0);
const root: boolean = plugin.tags.some((tag) => tag === 'root');
const { t } = useTranslation();
return (
<div
className="deckyStoreCard"
@@ -100,7 +100,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
plugin.description
) : (
<span>
<i style={{ color: '#666' }}>{t('plugin_no_desc')}</i>
<i style={{ color: '#666' }}>{t('PluginCard.plugin_no_desc')}</i>
</span>
)}
</span>
@@ -112,7 +112,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
color: '#fee75c',
}}
>
<i>{t('plugin_full_access')}</i>{' '}
<i>{t('PluginCard.plugin_full_access')}</i>{' '}
<a
className="deckyStoreCardDescriptionRootLink"
href="https://deckbrew.xyz/root"
@@ -149,7 +149,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
layout="below"
onClick={() => requestPluginInstall(plugin.name, plugin.versions[selectedOption])}
>
<span className="deckyStoreCardInstallText">{t('plugin_install')}</span>
<span className="deckyStoreCardInstallText">{t('PluginCard.plugin_install')}</span>
</ButtonItem>
</div>
<div
@@ -166,7 +166,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
label: version.name,
})) as SingleDropdownOption[]
}
menuLabel={t('plugin_version_label') as string}
menuLabel={t('PluginCard.plugin_version_label') as string}
selectedOption={selectedOption}
onChange={({ data }) => setSelectedOption(data)}
/>
+26 -22
View File
@@ -16,8 +16,6 @@ import Logger from '../../logger';
import { StorePlugin, getPluginList } from '../../store';
import PluginCard from './PluginCard';
const { t } = useTranslation('Store');
const logger = new Logger('FilePicker');
const StorePage: FC<{}> = () => {
@@ -28,6 +26,8 @@ const StorePage: FC<{}> = () => {
return false;
});
const { t } = useTranslation();
useEffect(() => {
(async () => {
const res = await getPluginList();
@@ -57,13 +57,13 @@ const StorePage: FC<{}> = () => {
}}
tabs={[
{
title: t('store_tabs_title'),
title: t('Store.store_tabs.title'),
content: <BrowseTab children={{ data: data }} />,
id: 'browse',
renderTabAddon: () => <span className={TabCount}>{data.length}</span>,
},
{
title: t('store_tabs.about'),
title: t('Store.store_tabs.about'),
content: <AboutTab />,
id: 'about',
},
@@ -76,10 +76,12 @@ const StorePage: FC<{}> = () => {
};
const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
const { t } = useTranslation();
const sortOptions = useMemo(
(): DropdownOption[] => [
{ data: 1, label: t('store_tabs.alph_desc') },
{ data: 2, label: t('store_tabs.alph_asce') },
{ data: 1, label: t('Store.store_tabs.alph_desc') },
{ data: 2, label: t('Store.store_tabs.alph_asce') },
],
[],
);
@@ -108,11 +110,11 @@ const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
width: '47.5%',
}}
>
<span className="DialogLabel">{t("store_sort.label")}</span>
<span className="DialogLabel">{t("Store.store_sort.label")}</span>
<Dropdown
menuLabel={t("store_sort.label") as string}
menuLabel={t("Store.store_sort.label") as string}
rgOptions={sortOptions}
strDefaultLabel={t("store_sort.label_def") as string}
strDefaultLabel={t("Store.store_sort.label_def") as string}
selectedOption={selectedSort}
onChange={(e) => setSort(e.data)}
/>
@@ -125,11 +127,11 @@ const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
marginLeft: 'auto',
}}
>
<span className="DialogLabel">{t("store_filter.label")}</span>
<span className="DialogLabel">{t("Store.store_filter.label")}</span>
<Dropdown
menuLabel={t("store_filter.label")}
menuLabel={t("Store.store_filter.label")}
rgOptions={filterOptions}
strDefaultLabel={t("store_filter.label_def")}
strDefaultLabel={t("Store.store_filter.label_def")}
selectedOption={selectedFilter}
onChange={(e) => setFilter(e.data)}
/>
@@ -139,7 +141,7 @@ const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
<div style={{ justifyContent: 'center', display: 'flex' }}>
<Focusable style={{ display: 'flex', alignItems: 'center', width: '96%' }}>
<div style={{ width: '100%' }}>
<TextField label={t("store_search.label")} value={searchFieldValue} onChange={(e) => setSearchValue(e.target.value)} />
<TextField label={t("Store.store_search.label")} value={searchFieldValue} onChange={(e) => setSearchValue(e.target.value)} />
</div>
</Focusable>
</div>
@@ -154,11 +156,11 @@ const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
maxWidth: '100%',
}}
>
<span className="DialogLabel">{t('store_sort.label')}</span>
<span className="DialogLabel">{t('Store.store_sort.label')}</span>
<Dropdown
menuLabel={t('store_sort.label') as string}
menuLabel={t('Store.store_sort.label') as string}
rgOptions={sortOptions}
strDefaultLabel={t('store_sort.label_def') as string}
strDefaultLabel={t('Store.store_sort.label_def') as string}
selectedOption={selectedSort}
onChange={(e) => setSort(e.data)}
/>
@@ -169,7 +171,7 @@ const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
<Focusable style={{ display: 'flex', alignItems: 'center', width: '96%' }}>
<div style={{ width: '100%' }}>
<TextField
label={t('store_search.label')}
label={t('Store.store_search.label')}
value={searchFieldValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
@@ -199,6 +201,8 @@ const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
};
const AboutTab: FC<{}> = () => {
const { t } = useTranslation();
return (
<div
style={{
@@ -223,7 +227,7 @@ const AboutTab: FC<{}> = () => {
/>
<span className="deckyStoreAboutHeader">Testing</span>
<span>
{t('store_testing_cta')}{' '}
{t('Store.store_testing_cta')}{' '}
<a
href="https://deckbrew.xyz/testing"
target="_blank"
@@ -234,10 +238,10 @@ const AboutTab: FC<{}> = () => {
deckbrew.xyz/testing
</a>
</span>
<span className="deckyStoreAboutHeader">{t('store_contrib.label')}</span>
<span>{t('store_contrib.desc')}</span>
<span className="deckyStoreAboutHeader">{t('store_source.label')}</span>
<span>{t('store_source.desc')}</span>
<span className="deckyStoreAboutHeader">{t('Store.store_contrib.label')}</span>
<span>{t('Store.store_contrib.desc')}</span>
<span className="deckyStoreAboutHeader">{t('Store.store_source.label')}</span>
<span>{t('Store.store_source.desc')}</span>
</div>
);
};
+5 -2
View File
@@ -18,6 +18,7 @@ import {
staticClasses,
updaterFieldClasses,
} from 'decky-frontend-lib';
import { useTranslation } from 'react-i18next';
import { FaReact } from 'react-icons/fa';
import Logger from './logger';
@@ -58,9 +59,11 @@ export async function setShowValveInternal(show: boolean) {
}
export async function setShouldConnectToReactDevTools(enable: boolean) {
const { t } = useTranslation();
window.DeckyPluginLoader.toaster.toast({
title: (enable ? 'Enabling' : 'Disabling') + ' React DevTools',
body: 'Reloading in 5 seconds',
title: (enable ? t('Developer.enabling') : t('Developer.disabling')) + ' React DevTools',
body: t('Developer.5secreload'),
icon: <FaReact />,
});
await sleep(5000);
+10 -2
View File
@@ -11,12 +11,20 @@ i18n
load: 'languageOnly',
debug: true,
fallbackLng: 'en',
lng: 'en',
lng: 'it',
interpolation: {
escapeValue: false,
},
backend: {
loadPath: 'http://127.0.0.1:1337/locales/{{lng}}/{{ns}}.json',
loadPath: 'http://127.0.0.1:1337/locales/{{lng}}.json',
requestOptions: {
// used for fetch
credentials: 'include',
cache: 'no-cache',
},
customHeaders: {
Authentication: window.deckyAuthToken,
},
},
});
+17 -11
View File
@@ -26,8 +26,6 @@ const SettingsPage = lazy(() => import('./components/settings'));
const FilePicker = lazy(() => import('./components/modals/filepicker'));
const { t } = useTranslation('plugin-loader');
declare global {
interface Window {}
}
@@ -103,10 +101,11 @@ class PluginLoader extends Logger {
public async notifyUpdates() {
const versionInfo = await this.updateVersion();
const { t } = useTranslation('PluginLoader');
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
this.toaster.toast({
title: 'Decky',
body: t('decky_update_available', versionInfo?.remote?.tag_name),
body: t('PluginLoader.decky_update_available', versionInfo?.remote?.tag_name),
onClick: () => Router.Navigate('/decky/settings'),
});
this.deckyState.setHasLoaderUpdate(true);
@@ -123,11 +122,12 @@ class PluginLoader extends Logger {
public async notifyPluginUpdates() {
const updates = await this.checkPluginUpdates();
const { t } = useTranslation();
if (updates?.size > 0) {
this.toaster.toast({
title: 'Decky',
//body: `Updates available for ${updates.size} plugin${updates.size > 1 ? 's' : ''}!`,
body: t('plugin_update', updates.size.toString(10), { count: updates.size }),
body: t('PluginLoader.plugin_update', updates.size.toString(10), { count: updates.size }),
onClick: () => Router.Navigate('/decky/settings/plugins'),
});
}
@@ -146,6 +146,7 @@ class PluginLoader extends Logger {
}
public uninstallPlugin(name: string) {
const { t } = useTranslation();
showModal(
<ConfirmModal
onOK={async () => {
@@ -154,10 +155,10 @@ class PluginLoader extends Logger {
onCancel={() => {
// do nothing
}}
strTitle={t('plugin_uninstall_title', name)}
strOKButtonText={t('plugin_uninstall_button')}
strTitle={t('PluginLoader.plugin_uninstall.title', name)}
strOKButtonText={t('PluginLoader.plugin_uninstall.button')}
>
{t('plugin_uninstall_desc', name)}
{t('PluginLoader.plugin_uninstall.desc', name)}
</ConfirmModal>,
);
}
@@ -233,6 +234,9 @@ class PluginLoader extends Logger {
Authentication: window.deckyAuthToken,
},
});
const { t } = useTranslation();
if (res.ok) {
try {
let plugin_export = await eval(await res.text());
@@ -243,14 +247,14 @@ class PluginLoader extends Logger {
version: version,
});
} catch (e) {
this.error(t('plugin_load_error', name), e);
this.error(t('PluginLoader.plugin_load_error', name), e);
const TheError: FC<{}> = () => (
<>
{t('error')}:{' '}
<pre>
<code>{e instanceof Error ? e.stack : JSON.stringify(e)}</code>
</pre>
<>{t('plugin_error_uninstall', <FaCog style={{ display: 'inline' }} />)}</>
<>{t('PluginLoader.plugin_error_uninstall', <FaCog style={{ display: 'inline' }} />)}</>
</>
);
this.plugins.push({
@@ -260,7 +264,7 @@ class PluginLoader extends Logger {
icon: <FaExclamationCircle />,
});
this.toaster.toast({
title: t('error_loading_plugin_toast', name),
title: t('PluginLoader.error_loading_plugin.toast', name),
body: '' + e,
icon: <FaExclamationCircle />,
});
@@ -296,12 +300,14 @@ class PluginLoader extends Logger {
includeFiles?: boolean,
regex?: RegExp,
): Promise<{ path: string; realpath: string }> {
const { t } = useTranslation();
return new Promise((resolve, reject) => {
const Content = ({ closeModal }: { closeModal?: () => void }) => (
// Purposely outside of the FilePicker component as lazy-loaded ModalRoots don't focus correctly
<ModalRoot
onCancel={() => {
reject(t('file_picker_cancel_text'));
reject(t('PluginLoader.file_picker_cancel_text'));
closeModal?.();
}}
>