mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-14 09:25:47 +01:00
🐛 500 error when saving user settings, Language not applied after saving user preferences
This commit is contained in:
@@ -1,133 +0,0 @@
|
|||||||
import { Group, Select, Stack, Text } from '@mantine/core';
|
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
import { getCookie, setCookie } from 'cookies-next';
|
|
||||||
import { useSession } from 'next-auth/react';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { forwardRef, useState } from 'react';
|
|
||||||
import { api } from '~/utils/api';
|
|
||||||
|
|
||||||
import { COOKIE_LOCALE_KEY } from '../../../../../data/constants';
|
|
||||||
import { Language, getLanguageByCode } from '~/tools/language';
|
|
||||||
|
|
||||||
export default function LanguageSelect() {
|
|
||||||
const { data: sessionData } = useSession();
|
|
||||||
const { t, i18n } = useTranslation('settings/general/internationalization');
|
|
||||||
const { changeLanguage } = i18n;
|
|
||||||
const configLocale = getCookie(COOKIE_LOCALE_KEY);
|
|
||||||
const { locale, locales, pathname, query, asPath, push } = useRouter();
|
|
||||||
const [selectedLanguage, setSelectedLanguage] = useState<string>(
|
|
||||||
sessionData?.user.language ?? (configLocale as string) ?? locale ?? 'en'
|
|
||||||
);
|
|
||||||
const { mutateAsync } = api.user.changeLanguage.useMutation();
|
|
||||||
|
|
||||||
const data = locales
|
|
||||||
? locales.map((localeItem) => ({
|
|
||||||
value: localeItem,
|
|
||||||
label: getLanguageByCode(localeItem).originalName,
|
|
||||||
icon: getLanguageByCode(localeItem).emoji,
|
|
||||||
language: getLanguageByCode(localeItem),
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const onChangeSelect = (value: string) => {
|
|
||||||
setSelectedLanguage(value);
|
|
||||||
|
|
||||||
const newLanguage = getLanguageByCode(value);
|
|
||||||
changeLanguage(value)
|
|
||||||
.then(async () => {
|
|
||||||
setCookie(COOKIE_LOCALE_KEY, value, {
|
|
||||||
maxAge: 60 * 60 * 24 * 30,
|
|
||||||
sameSite: 'strict',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (sessionData?.user && new Date(sessionData.expires) > new Date()) {
|
|
||||||
await mutateAsync({
|
|
||||||
language: value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
push(
|
|
||||||
{
|
|
||||||
pathname,
|
|
||||||
query,
|
|
||||||
},
|
|
||||||
asPath,
|
|
||||||
{ locale: value }
|
|
||||||
);
|
|
||||||
|
|
||||||
showNotification({
|
|
||||||
title: 'Language changed',
|
|
||||||
message: `You changed the language to '${newLanguage.originalName}'`,
|
|
||||||
color: 'green',
|
|
||||||
autoClose: 5000,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showNotification({
|
|
||||||
title: 'Failed to change language',
|
|
||||||
message: `Failed to change to '${newLanguage.originalName}', Error:'${err}`,
|
|
||||||
color: 'red',
|
|
||||||
autoClose: 5000,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack>
|
|
||||||
<Select
|
|
||||||
icon={<Text>{getLanguageByCode(selectedLanguage).emoji}</Text>}
|
|
||||||
label={t('label')}
|
|
||||||
data={data}
|
|
||||||
itemComponent={SelectItem}
|
|
||||||
nothingFound="Nothing found"
|
|
||||||
onChange={onChangeSelect}
|
|
||||||
value={selectedLanguage}
|
|
||||||
defaultValue={locale}
|
|
||||||
searchable
|
|
||||||
filter={(value, item) => {
|
|
||||||
const selectItems = item as unknown as { value: string; language: Language };
|
|
||||||
return (
|
|
||||||
selectItems.language.originalName
|
|
||||||
.toLowerCase()
|
|
||||||
.trim()
|
|
||||||
.includes(value.toLowerCase().trim()) ||
|
|
||||||
selectItems.language.translatedName
|
|
||||||
.toLowerCase()
|
|
||||||
.trim()
|
|
||||||
.includes(value.toLowerCase().trim())
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
styles={{
|
|
||||||
icon: {
|
|
||||||
width: 42,
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
paddingLeft: '45px !important',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ItemProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
||||||
image: string;
|
|
||||||
language: Language;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SelectItem = forwardRef<HTMLDivElement, ItemProps>(
|
|
||||||
({ language, image, ...others }: ItemProps, ref) => (
|
|
||||||
<div ref={ref} {...others}>
|
|
||||||
<Group noWrap>
|
|
||||||
<Text>{language.emoji}</Text>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Text size="sm">
|
|
||||||
{language.originalName} ({language.translatedName})
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
@@ -11,9 +11,11 @@ import {
|
|||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { createFormContext } from '@mantine/form';
|
import { createFormContext } from '@mantine/form';
|
||||||
import { IconArrowLeft } from '@tabler/icons-react';
|
import { IconArrowLeft } from '@tabler/icons-react';
|
||||||
|
import { changeLanguage } from 'i18next';
|
||||||
import { GetServerSideProps } from 'next';
|
import { GetServerSideProps } from 'next';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { AccessibilitySettings } from '~/components/User/Preferences/AccessibilitySettings';
|
import { AccessibilitySettings } from '~/components/User/Preferences/AccessibilitySettings';
|
||||||
@@ -76,9 +78,10 @@ const SettingsComponent = ({
|
|||||||
country: language.country,
|
country: language.country,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { t } = useTranslation(['user/preferences', 'common']);
|
const { t, i18n } = useTranslation(['user/preferences', 'common']);
|
||||||
|
|
||||||
const { i18nZodResolver } = useI18nZodResolver();
|
const { i18nZodResolver } = useI18nZodResolver();
|
||||||
|
const { pathname, query, asPath, push } = useRouter();
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
@@ -105,7 +108,22 @@ const SettingsComponent = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = (values: z.infer<typeof updateSettingsValidationSchema>) => {
|
const handleSubmit = (values: z.infer<typeof updateSettingsValidationSchema>) => {
|
||||||
mutate(values);
|
mutate(values, {
|
||||||
|
onSuccess: () => {
|
||||||
|
if (values.language !== settings.language) {
|
||||||
|
i18n.changeLanguage(values.language).then(() => {
|
||||||
|
push(
|
||||||
|
{
|
||||||
|
pathname,
|
||||||
|
query,
|
||||||
|
},
|
||||||
|
asPath,
|
||||||
|
{ locale: values.language }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -157,7 +157,16 @@ export const userRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
await db
|
await db
|
||||||
.update(userSettings)
|
.update(userSettings)
|
||||||
.set(input)
|
.set({
|
||||||
|
autoFocusSearch: input.autoFocusSearch,
|
||||||
|
defaultBoard: input.defaultBoard,
|
||||||
|
disablePingPulse: input.disablePingPulse,
|
||||||
|
firstDayOfWeek: input.firstDayOfWeek,
|
||||||
|
language: input.language,
|
||||||
|
openSearchInNewTab: input.openSearchInNewTab,
|
||||||
|
replacePingWithIcons: input.replaceDotsWithIcons,
|
||||||
|
searchTemplate: input.searchTemplate,
|
||||||
|
})
|
||||||
.where(eq(userSettings.userId, ctx.session?.user?.id));
|
.where(eq(userSettings.userId, ctx.session?.user?.id));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user