From a908c07670532b0ca7f766065bb4653ce2376e6f Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 30 Mar 2021 19:48:10 +0900 Subject: [PATCH] feat(frontend): allow selecting multiple original languages --- package.json | 2 + src/components/LanguageSelector/index.tsx | 194 ++++++++++++++++++ src/components/Settings/SettingsMain.tsx | 27 +-- .../UserGeneralSettings/index.tsx | 48 +---- src/i18n/locale/en.json | 3 +- src/styles/globals.css | 53 +++++ src/types/custom.d.ts | 8 + yarn.lock | 103 +++++++++- 8 files changed, 367 insertions(+), 71 deletions(-) create mode 100644 src/components/LanguageSelector/index.tsx diff --git a/package.json b/package.json index 4efe95608..ba22fde61 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "react-intersection-observer": "^8.31.0", "react-intl": "5.13.5", "react-markdown": "^5.0.3", + "react-select": "^4.3.0", "react-spring": "^8.0.27", "react-toast-notifications": "^2.4.3", "react-transition-group": "^4.4.1", @@ -99,6 +100,7 @@ "@types/nodemailer": "^6.4.1", "@types/react": "^17.0.3", "@types/react-dom": "^17.0.3", + "@types/react-select": "^4.0.13", "@types/react-toast-notifications": "^2.4.0", "@types/react-transition-group": "^4.4.1", "@types/secure-random-password": "^0.2.0", diff --git a/src/components/LanguageSelector/index.tsx b/src/components/LanguageSelector/index.tsx new file mode 100644 index 000000000..89392b319 --- /dev/null +++ b/src/components/LanguageSelector/index.tsx @@ -0,0 +1,194 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import dynamic from 'next/dynamic'; +import React from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import type { OptionsType, OptionTypeBase } from 'react-select'; +import { Language } from '../../../server/lib/settings'; +import globalMessages from '../../i18n/globalMessages'; + +const messages = defineMessages({ + originalLanguageDefault: 'All Languages', + languageServerDefault: 'Default ({language})', +}); + +const Select = dynamic(() => import('react-select'), { ssr: false }); + +type OptionType = { + value: string; + label: string; + isFixed?: boolean; +}; + +const selectStyles = { + multiValueLabel: (base: any, state: { data: { isFixed?: boolean } }) => { + return state.data.isFixed ? { ...base, paddingRight: 6 } : base; + }, + multiValueRemove: (base: any, state: { data: { isFixed?: boolean } }) => { + return state.data.isFixed ? { ...base, display: 'none' } : base; + }, +}; + +interface LanguageSelectorProps { + languages: Language[]; + value?: string; + setFieldValue: (property: string, value: string) => void; + serverValue?: string; + isUserSettings?: boolean; +} + +const LanguageSelector: React.FC = ({ + languages, + value, + setFieldValue, + serverValue, + isUserSettings = false, +}) => { + const intl = useIntl(); + + const defaultLanguageNameFallback = serverValue + ? languages.find((language) => language.iso_639_1 === serverValue) + ?.english_name ?? serverValue + : undefined; + + const options: OptionType[] = + languages.map((language) => ({ + label: + intl.formatDisplayName(language.iso_639_1, { + type: 'language', + fallback: 'none', + }) ?? language.english_name, + value: language.iso_639_1, + })) ?? []; + + if (isUserSettings) { + options.unshift({ + value: 'server', + label: intl.formatMessage(messages.languageServerDefault, { + language: serverValue + ? serverValue + .split('|') + .map( + (value) => + intl.formatDisplayName(value, { + type: 'language', + fallback: 'none', + }) ?? defaultLanguageNameFallback + ) + .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 ( +