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