forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}