import {useCallback, useMemo, useState} from 'react' import {useWindowDimensions, View} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {languageName} from '#/locale/helpers' import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages' import {isNative, isWeb} from '#/platform/detection' import { useLanguagePrefs, useLanguagePrefsApi, } from '#/state/preferences/languages' import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {SearchInput} from '#/components/forms/SearchInput' import * as Toggle from '#/components/forms/Toggle' import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import {Text} from '#/components/Typography' export function PostLanguageSelectDialog({ control, }: { control: Dialog.DialogControlProps }) { const {height} = useWindowDimensions() const insets = useSafeAreaInsets() const renderErrorBoundary = useCallback( (error: any) => , [], ) return ( ) } export function DialogInner() { const control = Dialog.useDialogContext() const [headerHeight, setHeaderHeight] = useState(0) const allowedLanguages = useMemo(() => { const uniqueLanguagesMap = LANGUAGES.filter(lang => !!lang.code2).reduce( (acc, lang) => { acc[lang.code2] = lang return acc }, {} as Record, ) return Object.values(uniqueLanguagesMap) }, []) const langPrefs = useLanguagePrefs() const [checkedLanguagesCode2, setCheckedLanguagesCode2] = useState( langPrefs.postLanguage.split(',') || [langPrefs.primaryLanguage], ) const [search, setSearch] = useState('') const setLangPrefs = useLanguagePrefsApi() const t = useTheme() const {_} = useLingui() const handleClose = () => { control.close(() => { let langsString = checkedLanguagesCode2.join(',') if (!langsString) { langsString = langPrefs.primaryLanguage } setLangPrefs.setPostLanguage(langsString) }) } // NOTE(@elijaharita): Displayed languages are split into 3 lists for // ordering. const displayedLanguages = useMemo(() => { function mapCode2List(code2List: string[]) { return code2List.map(code2 => LANGUAGES_MAP_CODE2[code2]).filter(Boolean) } // NOTE(@elijaharita): Get recent language codes and map them to language // objects. Both the user account's saved language history and the current // checked languages are displayed here. const recentLanguagesCode2 = Array.from( new Set([...checkedLanguagesCode2, ...langPrefs.postLanguageHistory]), ).slice(0, 5) || [] const recentLanguages = mapCode2List(recentLanguagesCode2) // NOTE(@elijaharita): helper functions const matchesSearch = (lang: Language) => lang.name.toLowerCase().includes(search.toLowerCase()) const isChecked = (lang: Language) => checkedLanguagesCode2.includes(lang.code2) const isInRecents = (lang: Language) => recentLanguagesCode2.includes(lang.code2) const checkedRecent = recentLanguages.filter(isChecked) if (search) { // NOTE(@elijaharita): if a search is active, we ALWAYS show checked // items, as well as any items that match the search. const uncheckedRecent = recentLanguages .filter(lang => !isChecked(lang)) .filter(matchesSearch) const unchecked = allowedLanguages.filter(lang => !isChecked(lang)) const all = unchecked .filter(matchesSearch) .filter(lang => !isInRecents(lang)) return { all, checkedRecent, uncheckedRecent, } } else { // NOTE(@elijaharita): if no search is active, we show everything. const uncheckedRecent = recentLanguages.filter(lang => !isChecked(lang)) const all = allowedLanguages .filter(lang => !recentLanguagesCode2.includes(lang.code2)) .filter(lang => !isInRecents(lang)) return { all, checkedRecent, uncheckedRecent, } } }, [ allowedLanguages, search, langPrefs.postLanguageHistory, checkedLanguagesCode2, ]) const listHeader = ( setHeaderHeight(evt.nativeEvent.layout.height)}> Choose Post Languages Select up to 3 languages used in this post {isWeb && ( )} setSearch('')} /> ) const isCheckedRecentEmpty = displayedLanguages.checkedRecent.length > 0 || displayedLanguages.uncheckedRecent.length > 0 const isDisplayedLanguagesEmpty = displayedLanguages.all.length === 0 const flatListData = [ ...(isCheckedRecentEmpty ? [{type: 'header', label: _(msg`Recently used`)}] : []), ...displayedLanguages.checkedRecent.map(lang => ({type: 'item', lang})), ...displayedLanguages.uncheckedRecent.map(lang => ({type: 'item', lang})), ...(isDisplayedLanguagesEmpty ? [] : [{type: 'header', label: _(msg`All languages`)}]), ...displayedLanguages.all.map(lang => ({type: 'item', lang})), ] return ( { if (item.type === 'header') { return ( {item.label} ) } const lang = item.lang return ( {languageName(lang, langPrefs.appLanguage)} ) }} footer={ } /> ) } function DialogError({details}: {details?: string}) { const {_} = useLingui() const control = Dialog.useDialogContext() return ( ) }