Files
jellyseerr/src/components/LanguageSelector/index.tsx
Gauthier 989af67c0a refactor: update Next.js, React.js and Node.js (#815)
* refactor: update Next.js and React.js

* refactor: update Next.js images

* refactor: update ESLint rules and fix warnings/errors

* fix: remove old intl polyfill

* fix: add proper size to next/image components

* fix: adjust full-size for next/image components

* fix: temporary allow all domains for image optimization

* build: fixes an issue where dev env could lead to javascript heap out of memory

* fix: resolve webpack cache issue with country-flag-icons

* refactor: switch compiler from Babel to SWC

* fix: resize logo in sidebar

* fix: break word on long path to avoid text overflow

* chore: added sharp for production image optimisation

* fix: change extract script for i18n to a custom script

* fix: resolve GitHub CodeQL alert

* chore: temporarily remove builds for ARMv7

* fix: resize avatar images

* refactor: update Node.js to v20

* fix: resolve various UI issues

* build: migrate yarn to pnpm and restrict engine to node@^20.0.0

* ci: specify the pnpm version to use in workflow actions

* ci: fix typo in pnpm action-setup for cypress workflow

* test(cypress): use pnpm instead of yarn

* style: ran prettier on pnpm-lock

* ci(cypress): setup nodejs v20 in cypress workflow

* ci: pnpm cache to reduce install time

* ci: use sh shell to get pnpm store directory

* build(dockerfile): migrate to pnpm from yarn in docker builds

* build(dockerfile): copy the proper pnpm lockfile

* build: install pnpm for all platforms

* build(dockerfile): remove unnecessary `&&` on apk installation steps

* build: migrate pnpm 8 to 9

* build(dockerfile): add node-gyp back in

* build(dockerfile): install node-gyp through npm

* build(dockerfile): ignore scripts to not run husky install when devdependencies are pruned

* build: migrate to pnpm from yarn

* chore: remove a section that is no longer relevant

---------

Co-authored-by: fallenbagel <98979876+Fallenbagel@users.noreply.github.com>
2024-06-23 23:43:54 +02:00

177 lines
5.1 KiB
TypeScript

import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import type { Language } from '@server/lib/settings';
import { sortBy } from 'lodash';
import { useMemo } from 'react';
import { useIntl } from 'react-intl';
import type { CSSObjectWithLabel } from 'react-select';
import Select from 'react-select';
import useSWR from 'swr';
const messages = defineMessages('components.LanguageSelector', {
originalLanguageDefault: 'All Languages',
languageServerDefault: 'Default ({language})',
});
type OptionType = {
value: string;
label: string;
isFixed?: boolean;
};
const selectStyles = {
multiValueLabel: (base: CSSObjectWithLabel, props: { data: OptionType }) => {
return props.data?.isFixed ? { ...base, paddingRight: 6 } : base;
},
multiValueRemove: (base: CSSObjectWithLabel, props: { data: OptionType }) => {
return props.data?.isFixed ? { ...base, display: 'none' } : base;
},
};
interface LanguageSelectorProps {
value?: string;
setFieldValue: (property: string, value: string) => void;
serverValue?: string;
isUserSettings?: boolean;
}
const LanguageSelector = ({
value,
setFieldValue,
serverValue,
isUserSettings = false,
}: LanguageSelectorProps) => {
const intl = useIntl();
const { data: languages } = useSWR<Language[]>('/api/v1/languages');
const sortedLanguages = useMemo(() => {
languages?.forEach((language) => {
language.name =
intl.formatDisplayName(language.iso_639_1, {
type: 'language',
fallback: 'none',
}) ?? 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,
})) ?? [];
if (isUserSettings) {
options.unshift({
value: 'server',
label: intl.formatMessage(messages.languageServerDefault, {
language: serverValue
? serverValue
.split('|')
.map((value) => languageName(value))
.reduce((prev, curr) =>
intl.formatMessage(globalMessages.delimitedlist, {
a: prev,
b: curr,
})
)
: intl.formatMessage(messages.originalLanguageDefault),
}),
isFixed: true,
});
}
options.unshift({
value: 'all',
label: intl.formatMessage(messages.originalLanguageDefault),
isFixed: true,
});
return (
<Select<OptionType, true>
options={options}
isMulti
className="react-select-container"
classNamePrefix="react-select"
value={
(isUserSettings && value === 'all') || (!isUserSettings && !value)
? {
value: 'all',
label: intl.formatMessage(messages.originalLanguageDefault),
isFixed: true,
}
: (value === '' || !value || value === 'server') && isUserSettings
? {
value: 'server',
label: intl.formatMessage(messages.languageServerDefault, {
language: serverValue
? serverValue
.split('|')
.map((value) => languageName(value))
.reduce((prev, curr) =>
intl.formatMessage(globalMessages.delimitedlist, {
a: prev,
b: curr,
})
)
: intl.formatMessage(messages.originalLanguageDefault),
}),
isFixed: true,
}
: (value
?.split('|')
.map((code) => {
const matchedLanguage = sortedLanguages?.find(
(lang) => lang.iso_639_1 === code
);
if (!matchedLanguage) {
return undefined;
}
return {
label: matchedLanguage.name,
value: matchedLanguage.iso_639_1,
};
})
.filter((option) => option !== undefined) as OptionType[])
}
onChange={(value, options) => {
if (
(options &&
options.action === 'select-option' &&
options.option?.value === 'server') ||
value.every((v) => v.value === 'server')
) {
return setFieldValue('originalLanguage', '');
}
if (
(options &&
options.action === 'select-option' &&
options.option?.value === 'all') ||
value.every((v) => v.value === 'all')
) {
return setFieldValue('originalLanguage', isUserSettings ? 'all' : '');
}
setFieldValue(
'originalLanguage',
value
.map((lang) => lang.value)
.filter((v) => v !== 'all')
.join('|')
);
}}
styles={selectStyles}
/>
);
};
export default LanguageSelector;