Bluesky app fork with some witchin' additions 馃挮
at main 121 lines 4.2 kB view raw
1import {useMemo} from 'react' 2import {msg} from '@lingui/core/macro' 3import {useLingui} from '@lingui/react' 4import {Trans} from '@lingui/react/macro' 5 6import {languageName} from '#/locale/helpers' 7import {APP_LANGUAGES, LANGUAGES} from '#/locale/languages' 8import {useLanguagePrefs} from '#/state/preferences' 9import {atoms as a, native, platform, tokens} from '#/alf' 10import {Button, ButtonIcon, ButtonText} from '#/components/Button' 11import { 12 ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon, 13 ChevronTopBottom_Stroke2_Corner0_Rounded as ChevronUpDownIcon, 14} from '#/components/icons/Chevron' 15import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe' 16import * as Menu from '#/components/Menu' 17 18export function SearchLanguageDropdown({ 19 value, 20 onChange, 21}: { 22 value: string 23 onChange(value: string): void 24}) { 25 const {_} = useLingui() 26 const {appLanguage, contentLanguages, primaryLanguage} = useLanguagePrefs() 27 28 const languages = useMemo(() => { 29 return LANGUAGES.filter( 30 (lang, index, self) => 31 Boolean(lang.code2) && // reduce to the code2 varieties 32 index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen) 33 ) 34 .map(l => ({ 35 label: languageName(l, appLanguage), 36 value: l.code2, 37 key: l.code2 + l.code3, 38 })) 39 .sort((a, b) => { 40 // prioritize user's languages 41 const aIsUser = contentLanguages.includes(a.value) 42 const bIsUser = contentLanguages.includes(b.value) 43 if (aIsUser && !bIsUser) return -1 44 if (bIsUser && !aIsUser) return 1 45 // prioritize "common" langs in the network 46 const aIsCommon = !!APP_LANGUAGES.find( 47 al => 48 // skip `ast`, because it uses a 3-letter code which conflicts with `as` 49 // it begins with `a` anyway so still is top of the list 50 (al.code2 as string) !== 'ast' && al.code2.startsWith(a.value), 51 ) 52 const bIsCommon = !!APP_LANGUAGES.find( 53 al => 54 // ditto 55 (al.code2 as string) !== 'ast' && al.code2.startsWith(b.value), 56 ) 57 if (aIsCommon && !bIsCommon) return -1 58 if (bIsCommon && !aIsCommon) return 1 59 // fall back to alphabetical 60 return a.label.localeCompare(b.label, primaryLanguage) 61 }) 62 }, [appLanguage, contentLanguages, primaryLanguage]) 63 64 const currentLanguageLabel = 65 languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`) 66 67 return ( 68 <Menu.Root> 69 <Menu.Trigger 70 label={_( 71 msg`Filter search by language (currently: ${currentLanguageLabel})`, 72 )}> 73 {({props}) => ( 74 <Button 75 {...props} 76 label={props.accessibilityLabel} 77 size="small" 78 color={platform({native: 'primary', default: 'secondary'})} 79 variant={platform({native: 'ghost', default: 'solid'})} 80 style={native([ 81 a.py_sm, 82 a.px_sm, 83 {marginRight: tokens.space.sm * -1}, 84 ])}> 85 <ButtonIcon icon={EarthIcon} /> 86 <ButtonText>{currentLanguageLabel}</ButtonText> 87 <ButtonIcon 88 icon={platform({ 89 native: ChevronUpDownIcon, 90 default: ChevronDownIcon, 91 })} 92 /> 93 </Button> 94 )} 95 </Menu.Trigger> 96 <Menu.Outer> 97 <Menu.LabelText> 98 <Trans>Filter search by language</Trans> 99 </Menu.LabelText> 100 <Menu.Item label={_(msg`All languages`)} onPress={() => onChange('')}> 101 <Menu.ItemText> 102 <Trans>All languages</Trans> 103 </Menu.ItemText> 104 <Menu.ItemRadio selected={value === ''} /> 105 </Menu.Item> 106 <Menu.Divider /> 107 <Menu.Group> 108 {languages.map(lang => ( 109 <Menu.Item 110 key={lang.key} 111 label={lang.label} 112 onPress={() => onChange(lang.value)}> 113 <Menu.ItemText>{lang.label}</Menu.ItemText> 114 <Menu.ItemRadio selected={value === lang.value} /> 115 </Menu.Item> 116 ))} 117 </Menu.Group> 118 </Menu.Outer> 119 </Menu.Root> 120 ) 121}