diff --git a/src/base/components/inputs/LanguageSelect.tsx b/src/base/components/inputs/LanguageSelect.tsx index e542c140..815d7472 100644 --- a/src/base/components/inputs/LanguageSelect.tsx +++ b/src/base/components/inputs/LanguageSelect.tsx @@ -23,18 +23,22 @@ import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts'; import { languageSortComparator, toUniqueLanguageCodes } from '@/base/utils/Languages.ts'; import { usePrevious } from '@mantine/hooks'; +import { AwaitableComponent, type AwaitableComponentProps } from 'awaitable-component'; -interface IProps { +const LanguageSelectDialog = ({ + isVisible, + onDismiss, + onSubmit, + onExitComplete, + selectedLanguages, + languages, +}: AwaitableComponentProps & { selectedLanguages: string[]; - setSelectedLanguages: (languages: string[]) => void; languages: string[]; -} - -export function LanguageSelect({ selectedLanguages, setSelectedLanguages, languages }: IProps) { +}) => { const { t } = useLingui(); const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueLanguageCodes(selectedLanguages)); - const [open, setOpen] = useState(false); const previousSelectedLanguages = usePrevious(selectedLanguages); @@ -53,14 +57,8 @@ export function LanguageSelect({ selectedLanguages, setSelectedLanguages, langua [languages, tmpSelectedLanguages], ); - const handleCancel = () => { - setOpen(false); - setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages)); - }; - const handleOk = () => { - setOpen(false); - setSelectedLanguages(toUniqueLanguageCodes(tmpSelectedLanguages)); + onSubmit(toUniqueLanguageCodes(tmpSelectedLanguages)); }; const handleChange = (language: string, selected: boolean) => { @@ -72,45 +70,70 @@ export function LanguageSelect({ selectedLanguages, setSelectedLanguages, langua }; return ( - <> - - setOpen(true)} aria-label="display more actions" edge="end" color="inherit"> - - - - - {t`Allowed Languages`} - - languagesSortedBySelectState[index]} - itemContent={(_index, language) => ( - - + + {t`Allowed Languages`} + + languagesSortedBySelectState[index]} + itemContent={(_index, language) => ( + + - handleChange(language, e.target.checked)} - /> - - )} - /> - - - - - - - + handleChange(language, e.target.checked)} + /> + + )} + /> + + + + + + + ); +}; + +export function LanguageSelect({ + setSelectedLanguages, + ...props +}: { + selectedLanguages: string[]; + setSelectedLanguages: (languages: string[]) => void; + languages: string[]; +}) { + const { t } = useLingui(); + + return ( + + { + try { + const updatedSelectedLanguages = await AwaitableComponent.show(LanguageSelectDialog, props); + + setSelectedLanguages(updatedSelectedLanguages); + } catch (e) { + // ignore + } + }} + aria-label="display more actions" + edge="end" + color="inherit" + > + + + ); } diff --git a/src/features/source/components/SourceLanguageSelect.tsx b/src/features/source/components/SourceLanguageSelect.tsx index 4e8702cd..0ceb7fab 100644 --- a/src/features/source/components/SourceLanguageSelect.tsx +++ b/src/features/source/components/SourceLanguageSelect.tsx @@ -40,16 +40,21 @@ import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx import { makeToast } from '@/base/utils/Toast.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx'; -import { usePrevious } from '@mantine/hooks'; +import { AwaitableComponent, type AwaitableComponentProps } from 'awaitable-component'; -export const SourceLanguageSelect = ({ +const SourceLanguageSelectDialog = ({ + isVisible, + onDismiss, + onSubmit, + onExitComplete, selectedLanguages, - setSelectedLanguages, languages, sources, -}: { +}: AwaitableComponentProps<{ + selectedLanguages: string[]; + sourceEnabledStateMetaUpdatePayload: Parameters[0]; +}> & { selectedLanguages: string[]; - setSelectedLanguages: (languages: string[]) => void; languages: string[]; sources: (SourceIdInfo & SourceLanguageInfo & @@ -63,12 +68,6 @@ export const SourceLanguageSelect = ({ const [tmpSourceIdToEnabledState, setTmpSourceIdToEnabledState] = useState>({}); const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueLanguageCodes(selectedLanguages)); - const [open, setOpen] = useState(false); - - const previousSelectedLanguages = usePrevious(selectedLanguages); - if (previousSelectedLanguages && previousSelectedLanguages !== selectedLanguages) { - setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages)); - } const sourcesByLanguage = useMemo(() => Sources.groupByLanguage(sources), [sources]); @@ -109,18 +108,10 @@ export const SourceLanguageSelect = ({ useCallback((index) => flattenedSourcesByLanguages[index].id, [flattenedSourcesByLanguages]), ); - const handleCancel = () => { - setOpen(false); - - setTmpSourceIdToEnabledState({}); - setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages)); - }; - const handleOk = () => { - setOpen(false); - - batchUpdateSourceMetadata( - Object.entries(tmpSourceIdToEnabledState) + onSubmit({ + selectedLanguages: toUniqueLanguageCodes(tmpSelectedLanguages), + sourceEnabledStateMetaUpdatePayload: Object.entries(tmpSourceIdToEnabledState) .map(([sourceId, enabled]) => { const source = sources.find((sourceToEnable) => sourceToEnable.id === sourceId); @@ -134,9 +125,7 @@ export const SourceLanguageSelect = ({ }; }) .filter((entry) => entry !== null), - ).catch((e) => makeToast(t`Failed to save changes`, 'error', getErrorMessage(e))); - setTmpSourceIdToEnabledState({}); - setSelectedLanguages(toUniqueLanguageCodes(tmpSelectedLanguages)); + }); }; const handleChange = (language: string, selected: boolean) => { @@ -148,92 +137,127 @@ export const SourceLanguageSelect = ({ }; return ( - <> - - setOpen(true)} aria-label="display more actions" edge="end" color="inherit"> - - - - - {t`Allowed Languages`} - - {!languages.length && {t`No sources installed`}} - { - const language = languagesSortedBySelectState[index]; - const isEnabled = tmpSelectedLanguages.includes(language); + + {t`Allowed Languages`} + + {!languages.length && {t`No sources installed`}} + { + const language = languagesSortedBySelectState[index]; + const isEnabled = tmpSelectedLanguages.includes(language); - return ( - - - handleChange(language, e.target.checked)} - /> - - ); - }} - itemContent={(index) => { - const source = flattenedSourcesByLanguages[index]; + return ( + + + handleChange(language, e.target.checked)} + /> + + ); + }} + itemContent={(index) => { + const source = flattenedSourcesByLanguages[index]; - return ( - - - + + - - - - setTmpSourceIdToEnabledState({ - ...tmpSourceIdToEnabledState, - [source.id]: e.target.checked, - }) - } + }, + spinnerImageProps: { + ignoreQueue: true, + }, + }} /> - - ); - }} - /> - - - - - - - + + + + setTmpSourceIdToEnabledState({ + ...tmpSourceIdToEnabledState, + [source.id]: e.target.checked, + }) + } + /> + + ); + }} + /> + + + + + + + ); +}; + +export const SourceLanguageSelect = ({ + setSelectedLanguages, + ...props +}: { + selectedLanguages: string[]; + setSelectedLanguages: (languages: string[]) => Promise; + languages: string[]; + sources: (SourceIdInfo & + SourceLanguageInfo & + SourceNameInfo & + SourceDisplayNameInfo & + SourceIconInfo & + SourceMetaInfo)[]; +}) => { + const { t } = useLingui(); + + return ( + + { + try { + const { selectedLanguages: updatedSelectedLanguages, sourceEnabledStateMetaUpdatePayload } = + await AwaitableComponent.show(SourceLanguageSelectDialog, props); + + await Promise.all([ + setSelectedLanguages(updatedSelectedLanguages), + batchUpdateSourceMetadata(sourceEnabledStateMetaUpdatePayload), + ]).catch((e) => makeToast(t`Failed to save changes`, 'error', getErrorMessage(e))); + } catch (e) { + // Ignore + } + }} + aria-label="display more actions" + edge="end" + color="inherit" + > + + + ); }; diff --git a/src/features/source/services/Sources.ts b/src/features/source/services/Sources.ts index acff06c5..df0f3fbb 100644 --- a/src/features/source/services/Sources.ts +++ b/src/features/source/services/Sources.ts @@ -176,7 +176,7 @@ export class Sources { static useLanguages(): { languages: string[]; - setLanguages: (languages: string[]) => void; + setLanguages: (languages: string[]) => Promise; } { const { t } = useLingui(); const {