import { useCallback, useState } from 'react'; import type { ChangeEventHandler, FC } from 'react'; import type { IntlShape } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { createSelector } from '@reduxjs/toolkit'; import type { List as ImmutableList } from 'immutable'; import type { SelectItem } from '@/mastodon/components/dropdown_selector'; import type { RootState } from '@/mastodon/store'; import { useAppSelector } from '@/mastodon/store'; import { Section } from './section'; const messages = defineMessages({ rules: { id: 'about.rules', defaultMessage: 'Server rules' }, defaultLocale: { id: 'about.default_locale', defaultMessage: 'Default' }, }); interface RulesSectionProps { isLoading?: boolean; } interface BaseRule { text: string; hint: string; } interface Rule extends BaseRule { id: string; translations?: Record; } export const RulesSection: FC = ({ isLoading = false }) => { const intl = useIntl(); const [locale, setLocale] = useState(intl.locale); const rules = useAppSelector((state) => rulesSelector(state, locale)); const localeOptions = useAppSelector((state) => localeOptionsSelector(state, intl), ); const handleLocaleChange: ChangeEventHandler = useCallback( (e) => { setLocale(e.currentTarget.value); }, [], ); if (isLoading) { return
; } if (rules.length === 0) { return (

); } return (
    {rules.map((rule) => (
  1. {rule.text}
    {!!rule.hint &&
    {rule.hint}
    }
  2. ))}
); }; const selectRules = (state: RootState) => { const rules = state.server.getIn([ 'server', 'rules', ]) as ImmutableList | null; if (!rules) { return []; } return rules.toJS() as Rule[]; }; const rulesSelector = createSelector( [selectRules, (_state, locale: string) => locale], (rules, locale): Rule[] => { return rules.map((rule) => { const translations = rule.translations; // Handle cached responses from earlier versions if (!translations) { return rule; } const partialLocale = locale.split('-')[0]; if (partialLocale && translations[partialLocale]) { rule.text = translations[partialLocale].text; rule.hint = translations[partialLocale].hint; } if (translations[locale]) { rule.text = translations[locale].text; rule.hint = translations[locale].hint; } return rule; }); }, ); const localeOptionsSelector = createSelector( [selectRules, (_state, intl: IntlShape) => intl], (rules, intl): SelectItem[] => { const langs: Record = { default: { value: 'default', text: intl.formatMessage(messages.defaultLocale), }, }; // Use the default locale as a target to translate language names. const intlLocale = new Intl.DisplayNames(intl.locale, { type: 'language', }); for (const { translations } of rules) { for (const locale in translations) { if (langs[locale]) { continue; // Skip if already added } langs[locale] = { value: locale, text: intlLocale.of(locale) ?? locale, }; } } return Object.values(langs); }, );