fix: correctly fall back to English name in LanguageSelector (#1537)

* fix: correctly fall back to English name in LanguageSelector

* refactor: clean up language sort & name logic

* refactor: also clean up region sort & name logic

* refactor: use arrow functions
pull/1553/head
TheCatLady 3 years ago committed by GitHub
parent b7b55e275c
commit 189313e94a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,6 +14,7 @@ export interface Library {
export interface Region { export interface Region {
iso_3166_1: string; iso_3166_1: string;
english_name: string; english_name: string;
name?: string;
} }
export interface Language { export interface Language {

@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { sortBy } from 'lodash';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React, { useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import type { OptionsType, OptionTypeBase } from 'react-select'; import type { OptionsType, OptionTypeBase } from 'react-select';
import useSWR from 'swr';
import { Language } from '../../../server/lib/settings'; import { Language } from '../../../server/lib/settings';
import globalMessages from '../../i18n/globalMessages'; import globalMessages from '../../i18n/globalMessages';
@ -29,7 +31,6 @@ const selectStyles = {
}; };
interface LanguageSelectorProps { interface LanguageSelectorProps {
languages: Language[];
value?: string; value?: string;
setFieldValue: (property: string, value: string) => void; setFieldValue: (property: string, value: string) => void;
serverValue?: string; serverValue?: string;
@ -37,26 +38,33 @@ interface LanguageSelectorProps {
} }
const LanguageSelector: React.FC<LanguageSelectorProps> = ({ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
languages,
value, value,
setFieldValue, setFieldValue,
serverValue, serverValue,
isUserSettings = false, isUserSettings = false,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const { data: languages } = useSWR<Language[]>('/api/v1/languages');
const defaultLanguageNameFallback = serverValue const sortedLanguages = useMemo(() => {
? languages.find((language) => language.iso_639_1 === serverValue) languages?.forEach((language) => {
?.english_name ?? serverValue language.name =
: undefined;
const options: OptionType[] =
languages.map((language) => ({
label:
intl.formatDisplayName(language.iso_639_1, { intl.formatDisplayName(language.iso_639_1, {
type: 'language', type: 'language',
fallback: 'none', fallback: 'none',
}) ?? language.english_name, }) ?? language.english_name;
});
return sortBy(languages, 'name');
}, [intl, languages]);
const languageName = (languageCode: string) =>
sortedLanguages?.find((language) => language.iso_639_1 === languageCode)
?.name ?? languageCode;
const options: OptionType[] =
sortedLanguages?.map((language) => ({
label: language.name,
value: language.iso_639_1, value: language.iso_639_1,
})) ?? []; })) ?? [];
@ -67,13 +75,7 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
language: serverValue language: serverValue
? serverValue ? serverValue
.split('|') .split('|')
.map( .map((value) => languageName(value))
(value) =>
intl.formatDisplayName(value, {
type: 'language',
fallback: 'none',
}) ?? defaultLanguageNameFallback
)
.reduce((prev, curr) => .reduce((prev, curr) =>
intl.formatMessage(globalMessages.delimitedlist, { intl.formatMessage(globalMessages.delimitedlist, {
a: prev, a: prev,
@ -112,13 +114,7 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
language: serverValue language: serverValue
? serverValue ? serverValue
.split('|') .split('|')
.map( .map((value) => languageName(value))
(value) =>
intl.formatDisplayName(value, {
type: 'language',
fallback: 'none',
}) ?? defaultLanguageNameFallback
)
.reduce((prev, curr) => .reduce((prev, curr) =>
intl.formatMessage(globalMessages.delimitedlist, { intl.formatMessage(globalMessages.delimitedlist, {
a: prev, a: prev,
@ -130,7 +126,7 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
isFixed: true, isFixed: true,
} }
: value?.split('|').map((code) => { : value?.split('|').map((code) => {
const matchedLanguage = languages.find( const matchedLanguage = sortedLanguages?.find(
(lang) => lang.iso_639_1 === code (lang) => lang.iso_639_1 === code
); );
@ -139,11 +135,7 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
} }
return { return {
label: label: matchedLanguage.name,
intl.formatDisplayName(matchedLanguage.iso_639_1, {
type: 'language',
fallback: 'none',
}) ?? matchedLanguage.english_name,
value: matchedLanguage.iso_639_1, value: matchedLanguage.iso_639_1,
}; };
}) ?? undefined }) ?? undefined

@ -2,6 +2,7 @@ import { Listbox, Transition } from '@headlessui/react';
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/solid'; import { CheckIcon, ChevronDownIcon } from '@heroicons/react/solid';
import { hasFlag } from 'country-flag-icons'; import { hasFlag } from 'country-flag-icons';
import 'country-flag-icons/3x2/flags.css'; import 'country-flag-icons/3x2/flags.css';
import { sortBy } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr'; import useSWR from 'swr';
@ -39,32 +40,21 @@ const RegionSelector: React.FC<RegionSelectorProps> = ({
[] []
); );
const sortedRegions = useMemo( const sortedRegions = useMemo(() => {
() => regions?.forEach((region) => {
regions?.sort((region1, region2) => { region.name =
const region1Name = intl.formatDisplayName(region.iso_3166_1, {
intl.formatDisplayName(region1.iso_3166_1, {
type: 'region', type: 'region',
fallback: 'none', fallback: 'none',
}) ?? region1.english_name; }) ?? region.english_name;
const region2Name = });
intl.formatDisplayName(region2.iso_3166_1, {
type: 'region',
fallback: 'none',
}) ?? region2.english_name;
return region1Name === region2Name return sortBy(regions, 'name');
? 0 }, [intl, regions]);
: region1Name > region2Name
? 1
: -1;
}),
[intl, regions]
);
const defaultRegionNameFallback = const regionName = (regionCode: string) =>
regions?.find((region) => region.iso_3166_1 === currentSettings.region) sortedRegions?.find((region) => region.iso_3166_1 === regionCode)?.name ??
?.english_name ?? currentSettings.region; regionCode;
useEffect(() => { useEffect(() => {
if (regions && value) { if (regions && value) {
@ -110,17 +100,11 @@ const RegionSelector: React.FC<RegionSelectorProps> = ({
)} )}
<span className="block truncate"> <span className="block truncate">
{selectedRegion && selectedRegion.iso_3166_1 !== 'all' {selectedRegion && selectedRegion.iso_3166_1 !== 'all'
? intl.formatDisplayName(selectedRegion.iso_3166_1, { ? regionName(selectedRegion.iso_3166_1)
type: 'region',
fallback: 'none',
}) ?? selectedRegion.english_name
: isUserSetting && selectedRegion?.iso_3166_1 !== 'all' : isUserSetting && selectedRegion?.iso_3166_1 !== 'all'
? intl.formatMessage(messages.regionServerDefault, { ? intl.formatMessage(messages.regionServerDefault, {
region: currentSettings.region region: currentSettings.region
? intl.formatDisplayName(currentSettings.region, { ? regionName(currentSettings.region)
type: 'region',
fallback: 'none',
}) ?? defaultRegionNameFallback
: intl.formatMessage(messages.regionDefault), : intl.formatMessage(messages.regionDefault),
}) })
: intl.formatMessage(messages.regionDefault)} : intl.formatMessage(messages.regionDefault)}
@ -168,13 +152,7 @@ const RegionSelector: React.FC<RegionSelectorProps> = ({
> >
{intl.formatMessage(messages.regionServerDefault, { {intl.formatMessage(messages.regionServerDefault, {
region: currentSettings.region region: currentSettings.region
? intl.formatDisplayName( ? regionName(currentSettings.region)
currentSettings.region,
{
type: 'region',
fallback: 'none',
}
) ?? defaultRegionNameFallback
: intl.formatMessage(messages.regionDefault), : intl.formatMessage(messages.regionDefault),
})} })}
</span> </span>
@ -241,10 +219,7 @@ const RegionSelector: React.FC<RegionSelectorProps> = ({
selected ? 'font-semibold' : 'font-normal' selected ? 'font-semibold' : 'font-normal'
} block truncate`} } block truncate`}
> >
{intl.formatDisplayName(region.iso_3166_1, { {regionName(region.iso_3166_1)}
type: 'region',
fallback: 'none',
}) ?? region.english_name}
</span> </span>
{selected && ( {selected && (
<span <span

@ -1,12 +1,12 @@
import { RefreshIcon } from '@heroicons/react/solid'; import { RefreshIcon } from '@heroicons/react/solid';
import axios from 'axios'; import axios from 'axios';
import { Field, Form, Formik } from 'formik'; import { Field, Form, Formik } from 'formik';
import React, { useMemo } from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr'; import useSWR, { mutate } from 'swr';
import * as Yup from 'yup'; import * as Yup from 'yup';
import type { Language, MainSettings } from '../../../server/lib/settings'; import type { MainSettings } from '../../../server/lib/settings';
import { Permission, useUser } from '../../hooks/useUser'; import { Permission, useUser } from '../../hooks/useUser';
import globalMessages from '../../i18n/globalMessages'; import globalMessages from '../../i18n/globalMessages';
import Badge from '../Common/Badge'; import Badge from '../Common/Badge';
@ -58,9 +58,7 @@ const SettingsMain: React.FC = () => {
const { data, error, revalidate } = useSWR<MainSettings>( const { data, error, revalidate } = useSWR<MainSettings>(
'/api/v1/settings/main' '/api/v1/settings/main'
); );
const { data: languages, error: languagesError } = useSWR<Language[]>(
'/api/v1/languages'
);
const MainSettingsSchema = Yup.object().shape({ const MainSettingsSchema = Yup.object().shape({
applicationTitle: Yup.string().required( applicationTitle: Yup.string().required(
intl.formatMessage(messages.validationApplicationTitle) intl.formatMessage(messages.validationApplicationTitle)
@ -96,26 +94,7 @@ const SettingsMain: React.FC = () => {
} }
}; };
const sortedLanguages = useMemo( if (!data && !error) {
() =>
languages?.sort((lang1, lang2) => {
const lang1Name =
intl.formatDisplayName(lang1.iso_639_1, {
type: 'language',
fallback: 'none',
}) ?? lang1.english_name;
const lang2Name =
intl.formatDisplayName(lang2.iso_639_1, {
type: 'language',
fallback: 'none',
}) ?? lang2.english_name;
return lang1Name === lang2Name ? 0 : lang1Name > lang2Name ? 1 : -1;
}),
[intl, languages]
);
if (!data && !error && !languages && !languagesError) {
return <LoadingSpinner />; return <LoadingSpinner />;
} }
@ -316,7 +295,6 @@ const SettingsMain: React.FC = () => {
<div className="form-input"> <div className="form-input">
<div className="form-input-field"> <div className="form-input-field">
<LanguageSelector <LanguageSelector
languages={sortedLanguages ?? []}
setFieldValue={setFieldValue} setFieldValue={setFieldValue}
value={values.originalLanguage} value={values.originalLanguage}
/> />

@ -1,12 +1,11 @@
import axios from 'axios'; import axios from 'axios';
import { Field, Form, Formik } from 'formik'; import { Field, Form, Formik } from 'formik';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr'; import useSWR from 'swr';
import { UserSettingsGeneralResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import { UserSettingsGeneralResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
import { Language } from '../../../../../server/lib/settings';
import { import {
availableLanguages, availableLanguages,
AvailableLocales, AvailableLocales,
@ -72,38 +71,11 @@ const UserGeneralSettings: React.FC = () => {
); );
}, [data]); }, [data]);
const { data: languages, error: languagesError } = useSWR<Language[]>(
'/api/v1/languages'
);
const sortedLanguages = useMemo(
() =>
languages?.sort((lang1, lang2) => {
const lang1Name =
intl.formatDisplayName(lang1.iso_639_1, {
type: 'language',
fallback: 'none',
}) ?? lang1.english_name;
const lang2Name =
intl.formatDisplayName(lang2.iso_639_1, {
type: 'language',
fallback: 'none',
}) ?? lang2.english_name;
return lang1Name === lang2Name ? 0 : lang1Name > lang2Name ? 1 : -1;
}),
[intl, languages]
);
if (!data && !error) { if (!data && !error) {
return <LoadingSpinner />; return <LoadingSpinner />;
} }
if (!languages && !languagesError) { if (!data) {
return <LoadingSpinner />;
}
if (!data || !languages) {
return <Error statusCode={500} />; return <Error statusCode={500} />;
} }
@ -263,7 +235,6 @@ const UserGeneralSettings: React.FC = () => {
<div className="form-input"> <div className="form-input">
<div className="form-input-field"> <div className="form-input-field">
<LanguageSelector <LanguageSelector
languages={sortedLanguages ?? []}
setFieldValue={setFieldValue} setFieldValue={setFieldValue}
serverValue={currentSettings.originalLanguage} serverValue={currentSettings.originalLanguage}
value={values.originalLanguage} value={values.originalLanguage}

Loading…
Cancel
Save