0
Fork 0
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:
Xiao Yijun 2022-10-13 11:23:19 +08:00 committed by GitHub
parent c887c5cecc
commit 68bda8aaea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 101 deletions

View file

@ -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,
};
};

View file

@ -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>
)}

View file

@ -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');

View file

@ -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}>

View file

@ -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 {