mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
refactor(console): create languages immediately on languages added in the editor (#2134)
This commit is contained in:
parent
c887c5cecc
commit
68bda8aaea
5 changed files with 52 additions and 101 deletions
|
@ -1,10 +1,11 @@
|
|||
import { LanguageTag } from '@logto/language-kit';
|
||||
import { builtInLanguages as builtInUiLanguages } from '@logto/phrases-ui';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { CustomPhraseResponse } from '@/types/custom-phrase';
|
||||
|
||||
import { RequestError } from './use-api';
|
||||
import useApi, { RequestError } from './use-api';
|
||||
|
||||
const useUiLanguages = () => {
|
||||
const {
|
||||
|
@ -26,11 +27,22 @@ const useUiLanguages = () => {
|
|||
[customPhraseList]
|
||||
);
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const addLanguage = useCallback(
|
||||
async (languageTag: LanguageTag) => {
|
||||
await api.put(`/api/custom-phrases/${languageTag}`, { json: {} });
|
||||
await mutate();
|
||||
},
|
||||
[api, mutate]
|
||||
);
|
||||
|
||||
return {
|
||||
customPhrases: customPhraseList,
|
||||
languages,
|
||||
error,
|
||||
isLoading: !customPhraseList && !error,
|
||||
addLanguage,
|
||||
mutate,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ import Button from '@/components/Button';
|
|||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import useApi, { RequestError } from '@/hooks/use-api';
|
||||
import useUiLanguages from '@/hooks/use-ui-languages';
|
||||
import { CustomPhraseResponse } from '@/types/custom-phrase';
|
||||
|
||||
import { createEmptyUiTranslation, flattenTranslation } from '../../../utilities';
|
||||
|
@ -29,8 +30,9 @@ const LanguageDetails = () => {
|
|||
|
||||
const { data: signInExperience } = useSWR<SignInExperience, RequestError>('/api/sign-in-exp');
|
||||
|
||||
const { languages, selectedLanguage, setIsDirty, setSelectedLanguage, stopAddingLanguage } =
|
||||
useContext(LanguageEditorContext);
|
||||
const { languages } = useUiLanguages();
|
||||
|
||||
const { selectedLanguage, setIsDirty, setSelectedLanguage } = useContext(LanguageEditorContext);
|
||||
|
||||
const [isDeletionAlertOpen, setIsDeletionAlertOpen] = useState(false);
|
||||
|
||||
|
@ -105,32 +107,11 @@ const LanguageDetails = () => {
|
|||
|
||||
void globalMutate('/api/custom-phrases');
|
||||
|
||||
stopAddingLanguage();
|
||||
|
||||
return updatedCustomPhrase;
|
||||
},
|
||||
[api, globalMutate, stopAddingLanguage]
|
||||
[api, globalMutate]
|
||||
);
|
||||
|
||||
const onDelete = useCallback(() => {
|
||||
if (!customPhrase && !isDefaultLanguage) {
|
||||
stopAddingLanguage(true);
|
||||
setSelectedLanguage(
|
||||
languages.find((languageTag) => languageTag !== selectedLanguage) ?? 'en'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
setIsDeletionAlertOpen(true);
|
||||
}, [
|
||||
customPhrase,
|
||||
isDefaultLanguage,
|
||||
languages,
|
||||
selectedLanguage,
|
||||
setSelectedLanguage,
|
||||
stopAddingLanguage,
|
||||
]);
|
||||
|
||||
const onConfirmDeletion = useCallback(async () => {
|
||||
setIsDeletionAlertOpen(false);
|
||||
|
||||
|
@ -176,7 +157,11 @@ const LanguageDetails = () => {
|
|||
)}
|
||||
</div>
|
||||
{!isBuiltIn && (
|
||||
<IconButton onClick={onDelete}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setIsDeletionAlertOpen(true);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
)}
|
||||
|
|
|
@ -5,22 +5,23 @@ import {
|
|||
} from '@logto/language-kit';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import useUiLanguages from '@/hooks/use-ui-languages';
|
||||
|
||||
import AddLanguageSelector from './AddLanguageSelector';
|
||||
import LanguageItem from './LanguageItem';
|
||||
import * as style from './LanguageNav.module.scss';
|
||||
import { LanguageEditorContext } from './use-language-editor-context';
|
||||
|
||||
const LanguageNav = () => {
|
||||
const { languages, addLanguage } = useUiLanguages();
|
||||
|
||||
const {
|
||||
languages,
|
||||
selectedLanguage,
|
||||
isAddingLanguage,
|
||||
isDirty,
|
||||
setConfirmationState,
|
||||
setSelectedLanguage,
|
||||
setPreSelectedLanguage,
|
||||
setPreAddedLanguage,
|
||||
startAddingLanguage,
|
||||
} = useContext(LanguageEditorContext);
|
||||
|
||||
const languageOptions = Object.keys(uiLanguageNameMapping).filter(
|
||||
|
@ -28,19 +29,20 @@ const LanguageNav = () => {
|
|||
isLanguageTag(languageTag) && !languages.includes(languageTag)
|
||||
);
|
||||
|
||||
const onAddLanguage = (languageTag: LanguageTag) => {
|
||||
if (isDirty || isAddingLanguage) {
|
||||
const onAddLanguage = async (languageTag: LanguageTag) => {
|
||||
if (isDirty) {
|
||||
setPreAddedLanguage(languageTag);
|
||||
setConfirmationState('try-add-language');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
startAddingLanguage(languageTag);
|
||||
await addLanguage(languageTag);
|
||||
setSelectedLanguage(languageTag);
|
||||
};
|
||||
|
||||
const onSwitchLanguage = (languageTag: LanguageTag) => {
|
||||
if (isDirty || isAddingLanguage) {
|
||||
if (isDirty) {
|
||||
setPreSelectedLanguage(languageTag);
|
||||
setConfirmationState('try-switch-language');
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useContext } from 'react';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
|
@ -19,23 +19,27 @@ type Props = {
|
|||
|
||||
const LanguageEditorModal = ({ isOpen, onClose }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { languages, addLanguage } = useUiLanguages();
|
||||
|
||||
const defaultSelectedLanguage = languages[0] ?? 'en';
|
||||
|
||||
const {
|
||||
languages,
|
||||
preSelectedLanguage,
|
||||
preAddedLanguage,
|
||||
isAddingLanguage,
|
||||
isDirty,
|
||||
confirmationState,
|
||||
setSelectedLanguage,
|
||||
setPreSelectedLanguage,
|
||||
setPreAddedLanguage,
|
||||
setConfirmationState,
|
||||
startAddingLanguage,
|
||||
stopAddingLanguage,
|
||||
} = useContext(LanguageEditorContext);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedLanguage(defaultSelectedLanguage);
|
||||
}, [defaultSelectedLanguage, setSelectedLanguage]);
|
||||
|
||||
const onCloseModal = () => {
|
||||
if (isAddingLanguage || isDirty) {
|
||||
if (isDirty) {
|
||||
setConfirmationState('try-close');
|
||||
|
||||
return;
|
||||
|
@ -45,9 +49,7 @@ const LanguageEditorModal = ({ isOpen, onClose }: Props) => {
|
|||
setSelectedLanguage(languages[0] ?? 'en');
|
||||
};
|
||||
|
||||
const onConfirmUnsavedChanges = () => {
|
||||
stopAddingLanguage(true);
|
||||
|
||||
const onConfirmUnsavedChanges = async () => {
|
||||
if (confirmationState === 'try-close') {
|
||||
onClose();
|
||||
}
|
||||
|
@ -58,7 +60,9 @@ const LanguageEditorModal = ({ isOpen, onClose }: Props) => {
|
|||
}
|
||||
|
||||
if (confirmationState === 'try-add-language' && preAddedLanguage) {
|
||||
startAddingLanguage(preAddedLanguage);
|
||||
await addLanguage(preAddedLanguage);
|
||||
setSelectedLanguage(preAddedLanguage);
|
||||
setPreAddedLanguage(undefined);
|
||||
}
|
||||
|
||||
setConfirmationState('none');
|
||||
|
@ -92,9 +96,8 @@ const LanguageEditorModal = ({ isOpen, onClose }: Props) => {
|
|||
};
|
||||
|
||||
const LanguageEditor = (props: Props) => {
|
||||
const { languages } = useUiLanguages();
|
||||
const { context: languageEditorContext, Provider: LanguageEditorContextProvider } =
|
||||
useLanguageEditorContext(languages);
|
||||
useLanguageEditorContext();
|
||||
|
||||
return (
|
||||
<LanguageEditorContextProvider value={languageEditorContext}>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { LanguageTag } from '@logto/language-kit';
|
||||
import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { createContext, useMemo, useState } from 'react';
|
||||
|
||||
const noop = () => {
|
||||
throw new Error('Context provider not found');
|
||||
|
@ -8,11 +8,9 @@ const noop = () => {
|
|||
export type ConfirmationState = 'none' | 'try-close' | 'try-switch-language' | 'try-add-language';
|
||||
|
||||
export type Context = {
|
||||
languages: LanguageTag[];
|
||||
selectedLanguage: LanguageTag;
|
||||
preSelectedLanguage?: LanguageTag;
|
||||
preAddedLanguage?: LanguageTag;
|
||||
isAddingLanguage: boolean;
|
||||
isDirty: boolean;
|
||||
confirmationState: ConfirmationState;
|
||||
setSelectedLanguage: React.Dispatch<React.SetStateAction<LanguageTag>>;
|
||||
|
@ -20,16 +18,12 @@ export type Context = {
|
|||
setPreAddedLanguage: React.Dispatch<React.SetStateAction<LanguageTag | undefined>>;
|
||||
setIsDirty: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setConfirmationState: React.Dispatch<React.SetStateAction<ConfirmationState>>;
|
||||
startAddingLanguage: (languageTag: LanguageTag) => void;
|
||||
stopAddingLanguage: (isCanceled?: boolean) => void;
|
||||
};
|
||||
|
||||
export const LanguageEditorContext = createContext<Context>({
|
||||
languages: [],
|
||||
selectedLanguage: 'en',
|
||||
preSelectedLanguage: undefined,
|
||||
preAddedLanguage: undefined,
|
||||
isAddingLanguage: false,
|
||||
isDirty: false,
|
||||
confirmationState: 'none',
|
||||
setSelectedLanguage: noop,
|
||||
|
@ -37,53 +31,20 @@ export const LanguageEditorContext = createContext<Context>({
|
|||
setPreAddedLanguage: noop,
|
||||
setIsDirty: noop,
|
||||
setConfirmationState: noop,
|
||||
startAddingLanguage: noop,
|
||||
stopAddingLanguage: noop,
|
||||
});
|
||||
|
||||
const useLanguageEditorContext = (defaultLanguages: LanguageTag[]) => {
|
||||
const [languages, setLanguages] = useState(defaultLanguages);
|
||||
|
||||
useEffect(() => {
|
||||
setLanguages(defaultLanguages);
|
||||
}, [defaultLanguages]);
|
||||
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<LanguageTag>(languages[0] ?? 'en');
|
||||
const useLanguageEditorContext = () => {
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<LanguageTag>('en');
|
||||
const [preSelectedLanguage, setPreSelectedLanguage] = useState<LanguageTag>();
|
||||
const [preAddedLanguage, setPreAddedLanguage] = useState<LanguageTag>();
|
||||
const [isAddingLanguage, setIsAddingLanguage] = useState(false);
|
||||
const [isDirty, setIsDirty] = useState(false);
|
||||
const [confirmationState, setConfirmationState] = useState<ConfirmationState>('none');
|
||||
|
||||
const startAddingLanguage = useCallback(
|
||||
(language: LanguageTag) => {
|
||||
setLanguages([...new Set([language, ...defaultLanguages])].slice().sort());
|
||||
setSelectedLanguage(language);
|
||||
setIsAddingLanguage(true);
|
||||
},
|
||||
[defaultLanguages]
|
||||
);
|
||||
|
||||
const stopAddingLanguage = useCallback(
|
||||
(isCanceled = false) => {
|
||||
if (isAddingLanguage) {
|
||||
if (isCanceled) {
|
||||
setLanguages(defaultLanguages);
|
||||
setSelectedLanguage(languages[0] ?? 'en');
|
||||
}
|
||||
setIsAddingLanguage(false);
|
||||
}
|
||||
},
|
||||
[defaultLanguages, isAddingLanguage, languages]
|
||||
);
|
||||
|
||||
const context = useMemo<Context>(
|
||||
() => ({
|
||||
languages,
|
||||
selectedLanguage,
|
||||
preSelectedLanguage,
|
||||
preAddedLanguage,
|
||||
isAddingLanguage,
|
||||
isDirty,
|
||||
confirmationState,
|
||||
setSelectedLanguage,
|
||||
|
@ -91,20 +52,8 @@ const useLanguageEditorContext = (defaultLanguages: LanguageTag[]) => {
|
|||
setPreAddedLanguage,
|
||||
setIsDirty,
|
||||
setConfirmationState,
|
||||
startAddingLanguage,
|
||||
stopAddingLanguage,
|
||||
}),
|
||||
[
|
||||
confirmationState,
|
||||
isAddingLanguage,
|
||||
isDirty,
|
||||
languages,
|
||||
preAddedLanguage,
|
||||
preSelectedLanguage,
|
||||
selectedLanguage,
|
||||
startAddingLanguage,
|
||||
stopAddingLanguage,
|
||||
]
|
||||
[confirmationState, isDirty, preAddedLanguage, preSelectedLanguage, selectedLanguage]
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
Loading…
Add table
Reference in a new issue